Bitcoin Core 32.99.0
P2P Digital Currency
Loading...
Searching...
No Matches
ipc_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2023-present 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/init.h>
6#include <ipc/capnp/mining.capnp.h>
8#include <interfaces/ipc.h>
9#include <ipc/process.h>
10#include <ipc/protocol.h>
11#include <ipc/test/ipc_test.capnp.h>
12#include <ipc/test/ipc_test.capnp.proxy.h>
13#include <ipc/test/ipc_test.h>
14#include <mp/proxy-types.h>
15#include <ipc/types.h>
16
17#include <test/util/common.h>
19#include <tinyformat.h>
20#include <util/log.h>
21#include <validation.h>
22
23#include <future>
24#include <optional>
25#include <thread>
26#include <kj/common.h>
27#include <kj/memory.h>
28#include <kj/test.h>
29#include <stdexcept>
30
31#include <boost/test/unit_test.hpp>
32
33static_assert(ipc::capnp::messages::MAX_MONEY == MAX_MONEY);
34static_assert(ipc::capnp::messages::MAX_DOUBLE == std::numeric_limits<double>::max());
35static_assert(ipc::capnp::messages::DEFAULT_BLOCK_RESERVED_WEIGHT == DEFAULT_BLOCK_RESERVED_WEIGHT);
36static_assert(ipc::capnp::messages::DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS == DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS);
37
40{
41public:
42 std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
43};
44
46static std::string TempPath(std::string_view pattern)
47{
48 std::string temp{fs::PathToString(fs::path{fs::temp_directory_path()} / fs::PathFromString(std::string{pattern}))};
49 temp.push_back('\0');
50 int fd{mkstemp(temp.data())};
51 BOOST_CHECK_GE(fd, 0);
52 BOOST_CHECK_EQUAL(close(fd), 0);
53 temp.resize(temp.size() - 1);
54 fs::remove(fs::PathFromString(temp));
55 return temp;
56}
57
67{
68 // Setup: create FooImplementation object and listen for FooInterface requests
69 std::promise<std::unique_ptr<mp::ProxyClient<gen::FooInterface>>> foo_promise;
70 std::thread thread([&]() {
71 mp::EventLoop loop("IpcPipeTest", [](bool raise, const std::string& log) { LogInfo("LOG%i: %s", raise, log); });
72 auto pipe = loop.m_io_context.provider->newTwoWayPipe();
73
74 auto connection_client = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[0]));
75 auto foo_client = std::make_unique<mp::ProxyClient<gen::FooInterface>>(
76 connection_client->m_rpc_system->bootstrap(mp::ServerVatId().vat_id).castAs<gen::FooInterface>(),
77 connection_client.get(), /* destroy_connection= */ true);
78 (void)connection_client.release();
79 foo_promise.set_value(std::move(foo_client));
80
81 auto connection_server = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[1]), [&](mp::Connection& connection) {
82 auto foo_server = kj::heap<mp::ProxyServer<gen::FooInterface>>(std::make_shared<FooImplementation>(), connection);
83 return capnp::Capability::Client(kj::mv(foo_server));
84 });
85 connection_server->onDisconnect([&] { connection_server.reset(); });
86 loop.loop();
87 });
88 std::unique_ptr<mp::ProxyClient<gen::FooInterface>> foo{foo_promise.get_future().get()};
89
90 // Test: make sure arguments were sent and return value is received
91 BOOST_CHECK_EQUAL(foo->add(1, 2), 3);
92
94 COutPoint txout2{foo->passOutPoint(txout1)};
96
98 uni1.pushKV("i", 1);
99 uni1.pushKV("s", "two");
100 UniValue uni2{foo->passUniValue(uni1)};
101 BOOST_CHECK_EQUAL(uni1.write(), uni2.write());
102
104 mtx.version = 2;
105 mtx.nLockTime = 3;
106 mtx.vin.emplace_back(txout1);
107 mtx.vout.emplace_back(COIN, CScript());
109 CTransactionRef tx2{foo->passTransaction(tx1)};
110 BOOST_CHECK(Assert(tx1)->Equals(*Assert(tx2)));
111
112 std::vector<CTransactionRef> txs1;
113 txs1.push_back(tx1);
114 txs1.push_back(nullptr);
115 std::vector<CTransactionRef> txs2(foo->passTransactions(txs1));
116 BOOST_CHECK_EQUAL(txs2.size(), 2);
117 BOOST_CHECK(Assert(txs1[0])->Equals(*Assert(txs2[0])));
118 BOOST_CHECK(!txs2[1]);
119
120 std::vector<char> vec1{'H', 'e', 'l', 'l', 'o'};
121 std::vector<char> vec2{foo->passVectorChar(vec1)};
122 BOOST_CHECK_EQUAL(std::string_view(vec1.begin(), vec1.end()), std::string_view(vec2.begin(), vec2.end()));
123
124 auto script1{CScript() << OP_11};
125 auto script2{foo->passScript(script1)};
127
128 // Test cleanup: disconnect and join thread
129 foo.reset();
130 thread.join();
131}
132
135{
136 std::unique_ptr<interfaces::Init> init{std::make_unique<TestInit>()};
137 std::unique_ptr<ipc::Protocol> protocol{ipc::capnp::MakeCapnpProtocol("IpcSocketPairTest")};
139 std::promise<void> promise;
140 std::thread thread([&]() {
141 protocol->serve(*init, [&] {
142 auto pair{mp::SocketPair()};
143 client_stream = protocol->makeStream(pair[0]);
144 promise.set_value();
145 return protocol->makeStream(pair[1]);
146 });
147 });
148 promise.get_future().wait();
149 std::unique_ptr<interfaces::Init> remote_init{protocol->connect(std::move(client_stream))};
150 std::unique_ptr<interfaces::Echo> remote_echo{remote_init->makeEcho()};
151 BOOST_CHECK_EQUAL(remote_echo->echo("echo test"), "echo test");
152 remote_echo.reset();
153 remote_init.reset();
154 thread.join();
155}
156
158void IpcSocketTest(const fs::path& datadir)
159{
160 std::unique_ptr<interfaces::Init> init{std::make_unique<TestInit>()};
161 std::unique_ptr<ipc::Protocol> protocol{ipc::capnp::MakeCapnpProtocol("IpcSocketTest")};
162 std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
163
164 std::string invalid_bind{"invalid:"};
165 BOOST_CHECK_THROW(process->bind(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
166 BOOST_CHECK_THROW(process->connect(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
167
168 auto bind_and_listen{[&](const std::string& bind_address) {
169 std::string address{bind_address};
170 mp::SocketId serve_fd = process->bind(datadir, "test_bitcoin", address);
173 protocol->listen(serve_fd, *init);
174 }};
175
176 auto connect_and_test{[&](const std::string& connect_address) {
177 std::string address{connect_address};
178 mp::SocketId connect_fd{process->connect(datadir, "test_bitcoin", address)};
180 std::unique_ptr<interfaces::Init> remote_init{protocol->connect(protocol->makeStream(connect_fd))};
181 std::unique_ptr<interfaces::Echo> remote_echo{remote_init->makeEcho()};
182 BOOST_CHECK_EQUAL(remote_echo->echo("echo test"), "echo test");
183 }};
184
185 // Need to specify explicit socket addresses outside the data directory, because the data
186 // directory path is so long that the default socket address and any other
187 // addresses in the data directory would fail with errors like:
188 // Address 'unix' path '"/tmp/test_common_Bitcoin Core/ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff/test_bitcoin.sock"' exceeded maximum socket path length
189 std::vector<std::string> addresses{
190 strprintf("unix:%s", TempPath("bitcoin_sock0_XXXXXX")),
191 strprintf("unix:%s", TempPath("bitcoin_sock1_XXXXXX")),
192 };
193
194 // Bind and listen on multiple addresses
195 for (const auto& address : addresses) {
196 bind_and_listen(address);
197 }
198
199 // Connect and test each address multiple times.
200 for (int i : {0, 1, 0, 0, 1}) {
202 }
203}
204
206void IpcSocketMaxConnectionsTest(const fs::path& datadir)
207{
208 std::unique_ptr<interfaces::Init> init{std::make_unique<TestInit>()};
209 std::unique_ptr<ipc::Protocol> protocol{ipc::capnp::MakeCapnpProtocol("IpcSocketMaxConnectionsTest")};
210 std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
211
212 std::string address{strprintf("unix:%s", TempPath("bitcoin_sock_limit_XXXXXX"))};
213 mp::SocketId serve_fd = process->bind(datadir, "test_bitcoin", address);
215 protocol->listen(serve_fd, *init, /*max_connections=*/1);
216
217 auto connect_fd{[&](const std::string& connect_address) {
218 std::string addr{connect_address};
219 mp::SocketId fd{process->connect(datadir, "test_bitcoin", addr)};
220 return std::make_pair(fd, addr);
221 }};
222
223 auto [fd1, addr1] = connect_fd(address);
224 BOOST_CHECK_EQUAL(addr1, address);
225 std::unique_ptr<interfaces::Init> remote_init1{protocol->connect(protocol->makeStream(fd1))};
226 std::unique_ptr<interfaces::Echo> remote_echo1{remote_init1->makeEcho()};
227 BOOST_CHECK_EQUAL(remote_echo1->echo("echo test 1"), "echo test 1");
228
229 auto second_call = std::async(std::launch::async, [&] {
230 std::string addr{address};
231 mp::SocketId fd{process->connect(datadir, "test_bitcoin", addr)};
232 std::unique_ptr<interfaces::Init> remote_init{protocol->connect(protocol->makeStream(fd))};
233 std::unique_ptr<interfaces::Echo> remote_echo{remote_init->makeEcho()};
234 return std::make_pair(remote_echo->echo("echo test 2"), addr);
235 });
236
237 BOOST_CHECK(second_call.wait_for(std::chrono::milliseconds{100}) == std::future_status::timeout);
238
239 remote_echo1.reset();
240 remote_init1.reset();
241
242 BOOST_REQUIRE(second_call.wait_for(std::chrono::seconds{5}) == std::future_status::ready);
243 auto [echo2, addr2] = second_call.get();
244 BOOST_CHECK_EQUAL(addr2, address);
245 BOOST_CHECK_EQUAL(echo2, "echo test 2");
246}
247
250{
251 IpcPipeTest();
253 IpcSocketTest(m_args.GetDataDirNet());
254 IpcSocketMaxConnectionsTest(m_args.GetDataDirNet());
255}
256
257// Test address parsing.
259{
260 std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
261 fs::path datadir{"/var/empty/notexist"};
262 auto check_notexist{[](const std::system_error& e) { return e.code() == std::errc::no_such_file_or_directory; }};
263 auto check_address{[&](std::string address, std::string expect_address, std::string expect_error) {
264 if (expect_error.empty()) {
265 BOOST_CHECK_EXCEPTION(process->connect(datadir, "test_bitcoin", address), std::system_error, check_notexist);
266 } else {
267 BOOST_CHECK_EXCEPTION(process->connect(datadir, "test_bitcoin", address), std::invalid_argument, HasReason(expect_error));
268 }
270 }};
271 std::string prefix{fs::PathToString(datadir / "")};
272 check_address("unix", "unix:" + prefix + "test_bitcoin.sock", "");
273 check_address("unix:", "unix:" + prefix + "test_bitcoin.sock", "");
274 check_address("unix:path.sock", "unix:" + prefix + "path.sock", "");
275 check_address("unix:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.sock",
276 "unix:" + prefix + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.sock",
277 "Unix address path \"" + prefix + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.sock\" exceeded maximum socket path length");
278 check_address("invalid", "invalid", "Unrecognized address 'invalid'");
279
280 auto check_listen_address{[](const std::string& configured_address, const std::string& expected_address, size_t expected_max_connections, const std::string& expected_error = "") {
282 if (!expected_error.empty()) {
285 return;
286 }
290 }};
293 check_listen_address("unix:,max-connections=8", "unix:", 8);
294 check_listen_address("unix:path.sock,max-connections=8", "unix:path.sock", 8);
295 check_listen_address("unix:,max-connections=1", "unix:", 1);
296 check_listen_address("unix:,max-connections=256", "unix:", ipc::MAX_CONNECTIONS);
297 check_listen_address("unix:path.sock,max-connections=8,unknown=1", "", 0, "Unknown socket option 'unknown'");
298 check_listen_address("unix:,max-connections=8,", "", 0, "Empty socket option");
299 check_listen_address("unix:,,max-connections=8", "", 0, "Empty socket option");
300 check_listen_address("unix:,max-connections=8,,max-connections=8", "", 0, "Empty socket option");
301 check_listen_address("unix:,max-connections=0", "", 0, "max-connections must be at least 1");
302 check_listen_address("unix:,max-connections=-1", "", 0, "max-connections must be at least 1");
303 check_listen_address("unix:,max-connections=-9223372036854775808", "", 0, "max-connections must be at least 1");
304 check_listen_address("unix:,max-connections=257", "", 0, "max-connections must be at most 256");
305 check_listen_address("unix:,max-connections=9223372036854775808", "", 0, "Invalid max-connections value '9223372036854775808'");
306 check_listen_address("unix:,max-connections=", "", 0, "Missing value for max-connections option");
307 check_listen_address("unix:path,backup.sock=1", "", 0, "Unknown socket option 'backup.sock'");
308 check_listen_address("unix:path%2Cbackup.sock=1", "unix:path,backup.sock=1", ipc::DEFAULT_MAX_CONNECTIONS);
309 check_listen_address("unix:path%2Cbackup.sock=1,max-connections=8", "unix:path,backup.sock=1", 8);
310 check_listen_address("unix:,max-connections", "", 0, "Missing value for max-connections option");
311 check_listen_address("unix:,max-connections=2,max-connections=1", "unix:", 1);
312}
313
314// Test command line parsing in ipc::Process::checkSpawned().
316{
317 std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
318 char arg0[]{"bitcoin-node"};
319 char arg_spawn[]{"-ipcchild"};
320 char arg_invalid[]{"invalid"};
321 char arg_other[]{"-ipcbind=unix"};
322
323 // no -ipcchild arg.
324 {
325 char* argv[]{arg0};
326 BOOST_CHECK(!process->checkSpawned(1, argv));
327 }
328 {
329 char* argv[]{arg0, arg_other};
330 BOOST_CHECK(!process->checkSpawned(2, argv));
331 }
332 {
333 char* argv[]{arg0, arg_other, arg_invalid};
334 BOOST_CHECK(!process->checkSpawned(3, argv));
335 }
336 // -ipcchild without a value.
337 {
338 char* argv[]{arg0, arg_spawn};
339 BOOST_CHECK(!process->checkSpawned(2, argv));
340 }
341 // -ipcchild combined with other arguments.
342 {
344 BOOST_CHECK(!process->checkSpawned(4, argv));
345 }
346 // -ipcchild with a value that is not a valid way of connecting to the
347 // parent process.
348 {
349 char* argv[]{arg0, arg_spawn, arg_invalid};
350 BOOST_CHECK_THROW(process->checkSpawned(3, argv), std::runtime_error);
351 }
352}
353
constexpr CAmount MAX_MONEY
No amount larger than this (in satoshi) is valid.
Definition amount.h:26
constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition amount.h:15
#define Assert(val)
Identity function.
Definition check.h:119
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition transaction.h:30
Serialized script, used inside transaction inputs and outputs.
Definition script.h:406
BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
Definition common.h:19
Remote init class.
Definition ipc_tests.cpp:40
std::unique_ptr< interfaces::Echo > makeEcho() override
Definition ipc_tests.cpp:42
void pushKV(std::string key, UniValue val)
Definition univalue.cpp:125
Initial interface created when a process is first started, and used to give and get access to other i...
Definition init.h:39
static util::Result< ipc::ListenAddress > parseListenAddress(std::string address)
Parse an IPC listening address and its per-listener options.
Definition listen.cpp:29
Object holding network & rpc state associated with either an incoming server connection,...
Definition proxy-io.h:436
Event loop implementation.
Definition proxy-io.h:242
kj::AsyncIoContext m_io_context
Capnp IO context.
Definition proxy-io.h:334
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition uint256.h:196
BOOST_FIXTURE_TEST_SUITE(cuckoocache_tests, BasicTestingSetup)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition fs.h:160
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition fs.h:183
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition hex_base.cpp:30
BOOST_CHECK_EXCEPTION(HTTPHeaders{}.Read(reader), std::runtime_error, HasReason{"Empty HTTP header name"})
is a home for public enum and struct type definitions that are used internally by ipc code,...
BOOST_AUTO_TEST_CASE(ipc_tests)
void IpcSocketMaxConnectionsTest(const fs::path &datadir)
Test ipc::Protocol listen() local connection limiting over a unix socket.
static std::string TempPath(std::string_view pattern)
Generate a temporary path with temp_directory_path and mkstemp.
Definition ipc_tests.cpp:46
void IpcSocketTest(const fs::path &datadir)
Test ipc::Process bind() and connect() methods connecting over a unix socket.
void IpcPipeTest()
Unit test that tests execution of IPC calls without actually creating a separate process.
Definition ipc_tests.cpp:66
void IpcSocketPairTest()
Test ipc::Protocol connect() and serve() methods connecting over a socketpair.
static const std::string addr1
Definition key_tests.cpp:31
static const std::string addr2
Definition key_tests.cpp:32
std::thread thread
Thread variable should be after other struct members so the thread does not start until the other mem...
#define LogInfo(...)
Definition log.h:136
Definition basic.cpp:11
std::unique_ptr< Echo > MakeEcho()
Return implementation of Echo interface.
std::unique_ptr< Protocol > MakeCapnpProtocol(const char *exe_name)
Definition protocol.cpp:160
constexpr size_t MAX_CONNECTIONS
Maximum accepted client connection limit for an IPC listening socket.
Definition types.h:27
std::unique_ptr< Process > MakeProcess()
Constructor for Process interface.
Definition process.cpp:157
constexpr size_t DEFAULT_MAX_CONNECTIONS
Default accepted client connection limit for each IPC listening socket.
Definition types.h:22
SocketId Stream
Definition util.h:30
int SocketId
Definition util.h:27
std::array< SocketId, 2 > SocketPair()
Definition util.h:36
constexpr SocketId SocketError
Definition util.h:28
bilingual_str ErrorString(const Result< T > &result)
Definition result.h:93
#define BOOST_CHECK_THROW(stmt, excMatch)
Definition object.cpp:18
#define BOOST_CHECK_EQUAL(v1, v2)
Definition object.cpp:17
#define BOOST_CHECK(expr)
Definition object.cpp:16
constexpr unsigned int DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS
Default sigops cost to reserve for coinbase transaction outputs when creating block templates.
Definition policy.h:29
constexpr unsigned int DEFAULT_BLOCK_RESERVED_WEIGHT
Default for -blockreservedweight.
Definition policy.h:27
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
const char * prefix
Definition rest.cpp:1197
@ OP_11
Definition script.h:95
Basic testing setup.
A mutable version of CTransaction.
Vat id for server side of connection.
Definition proxy-io.h:520
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
BOOST_CHECK_NE(OneL.ToString(), ArrayToString(ZeroArray, 32))
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:82