Bitcoin Core 32.99.0
P2P Digital Currency
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 <ipc/process.h>
9#include <ipc/protocol.h>
10#include <ipc/test/ipc_test.capnp.h>
11#include <ipc/test/ipc_test.capnp.proxy.h>
12#include <ipc/test/ipc_test.h>
13#include <mp/proxy-types.h>
14#include <test/util/common.h>
16#include <tinyformat.h>
17#include <util/log.h>
18#include <validation.h>
19
20#include <future>
21#include <optional>
22#include <thread>
23#include <kj/common.h>
24#include <kj/memory.h>
25#include <kj/test.h>
26#include <stdexcept>
27
28#include <boost/test/unit_test.hpp>
29
31static_assert(ipc::capnp::messages::MAX_DOUBLE == std::numeric_limits<double>::max());
34
37{
38public:
39 std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
40};
41
43static std::string TempPath(std::string_view pattern)
44{
45 std::string temp{fs::PathToString(fs::path{fs::temp_directory_path()} / fs::PathFromString(std::string{pattern}))};
46 temp.push_back('\0');
47 int fd{mkstemp(temp.data())};
48 BOOST_CHECK_GE(fd, 0);
49 BOOST_CHECK_EQUAL(close(fd), 0);
50 temp.resize(temp.size() - 1);
51 fs::remove(fs::PathFromString(temp));
52 return temp;
53}
54
64{
65 // Setup: create FooImplementation object and listen for FooInterface requests
66 std::promise<std::unique_ptr<mp::ProxyClient<gen::FooInterface>>> foo_promise;
67 std::thread thread([&]() {
68 mp::EventLoop loop("IpcPipeTest", [](bool raise, const std::string& log) { LogInfo("LOG%i: %s", raise, log); });
69 auto pipe = loop.m_io_context.provider->newTwoWayPipe();
70
71 auto connection_client = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[0]));
72 auto foo_client = std::make_unique<mp::ProxyClient<gen::FooInterface>>(
73 connection_client->m_rpc_system->bootstrap(mp::ServerVatId().vat_id).castAs<gen::FooInterface>(),
74 connection_client.get(), /* destroy_connection= */ true);
75 (void)connection_client.release();
76 foo_promise.set_value(std::move(foo_client));
77
78 auto connection_server = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[1]), [&](mp::Connection& connection) {
79 auto foo_server = kj::heap<mp::ProxyServer<gen::FooInterface>>(std::make_shared<FooImplementation>(), connection);
80 return capnp::Capability::Client(kj::mv(foo_server));
81 });
82 connection_server->onDisconnect([&] { connection_server.reset(); });
83 loop.loop();
84 });
85 std::unique_ptr<mp::ProxyClient<gen::FooInterface>> foo{foo_promise.get_future().get()};
86
87 // Test: make sure arguments were sent and return value is received
88 BOOST_CHECK_EQUAL(foo->add(1, 2), 3);
89
90 COutPoint txout1{Txid::FromUint256(uint256{100}), 200};
91 COutPoint txout2{foo->passOutPoint(txout1)};
92 BOOST_CHECK(txout1 == txout2);
93
95 uni1.pushKV("i", 1);
96 uni1.pushKV("s", "two");
97 UniValue uni2{foo->passUniValue(uni1)};
98 BOOST_CHECK_EQUAL(uni1.write(), uni2.write());
99
101 mtx.version = 2;
102 mtx.nLockTime = 3;
103 mtx.vin.emplace_back(txout1);
104 mtx.vout.emplace_back(COIN, CScript());
106 CTransactionRef tx2{foo->passTransaction(tx1)};
107 BOOST_CHECK(Assert(tx1)->Equals(*Assert(tx2)));
108
109 std::vector<CTransactionRef> txs1;
110 txs1.push_back(tx1);
111 txs1.push_back(nullptr);
112 std::vector<CTransactionRef> txs2(foo->passTransactions(txs1));
113 BOOST_CHECK_EQUAL(txs2.size(), 2);
114 BOOST_CHECK(Assert(txs1[0])->Equals(*Assert(txs2[0])));
115 BOOST_CHECK(!txs2[1]);
116
117 std::vector<char> vec1{'H', 'e', 'l', 'l', 'o'};
118 std::vector<char> vec2{foo->passVectorChar(vec1)};
119 BOOST_CHECK_EQUAL(std::string_view(vec1.begin(), vec1.end()), std::string_view(vec2.begin(), vec2.end()));
120
121 auto script1{CScript() << OP_11};
122 auto script2{foo->passScript(script1)};
123 BOOST_CHECK_EQUAL(HexStr(script1), HexStr(script2));
124
125 // Test cleanup: disconnect and join thread
126 foo.reset();
127 thread.join();
128}
129
132{
133 std::unique_ptr<interfaces::Init> init{std::make_unique<TestInit>()};
134 std::unique_ptr<ipc::Protocol> protocol{ipc::capnp::MakeCapnpProtocol("IpcSocketPairTest")};
135 mp::Stream client_stream;
136 std::promise<void> promise;
137 std::thread thread([&]() {
138 protocol->serve(*init, [&] {
139 auto pair{mp::SocketPair()};
140 client_stream = protocol->makeStream(pair[0]);
141 promise.set_value();
142 return protocol->makeStream(pair[1]);
143 });
144 });
145 promise.get_future().wait();
146 std::unique_ptr<interfaces::Init> remote_init{protocol->connect(std::move(client_stream))};
147 std::unique_ptr<interfaces::Echo> remote_echo{remote_init->makeEcho()};
148 BOOST_CHECK_EQUAL(remote_echo->echo("echo test"), "echo test");
149 remote_echo.reset();
150 remote_init.reset();
151 thread.join();
152}
153
155void IpcSocketTest(const fs::path& datadir)
156{
157 std::unique_ptr<interfaces::Init> init{std::make_unique<TestInit>()};
158 std::unique_ptr<ipc::Protocol> protocol{ipc::capnp::MakeCapnpProtocol("IpcSocketTest")};
159 std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
160
161 std::string invalid_bind{"invalid:"};
162 BOOST_CHECK_THROW(process->bind(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
163 BOOST_CHECK_THROW(process->connect(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
164
165 auto bind_and_listen{[&](const std::string& bind_address) {
166 std::string address{bind_address};
167 mp::SocketId serve_fd = process->bind(datadir, "test_bitcoin", address);
168 BOOST_CHECK_NE(serve_fd, mp::SocketError);
169 BOOST_CHECK_EQUAL(address, bind_address);
170 protocol->listen(serve_fd, *init);
171 }};
172
173 auto connect_and_test{[&](const std::string& connect_address) {
174 std::string address{connect_address};
175 mp::SocketId connect_fd{process->connect(datadir, "test_bitcoin", address)};
176 BOOST_CHECK_EQUAL(address, connect_address);
177 std::unique_ptr<interfaces::Init> remote_init{protocol->connect(protocol->makeStream(connect_fd))};
178 std::unique_ptr<interfaces::Echo> remote_echo{remote_init->makeEcho()};
179 BOOST_CHECK_EQUAL(remote_echo->echo("echo test"), "echo test");
180 }};
181
182 // Need to specify explicit socket addresses outside the data directory, because the data
183 // directory path is so long that the default socket address and any other
184 // addresses in the data directory would fail with errors like:
185 // Address 'unix' path '"/tmp/test_common_Bitcoin Core/ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff/test_bitcoin.sock"' exceeded maximum socket path length
186 std::vector<std::string> addresses{
187 strprintf("unix:%s", TempPath("bitcoin_sock0_XXXXXX")),
188 strprintf("unix:%s", TempPath("bitcoin_sock1_XXXXXX")),
189 };
190
191 // Bind and listen on multiple addresses
192 for (const auto& address : addresses) {
193 bind_and_listen(address);
194 }
195
196 // Connect and test each address multiple times.
197 for (int i : {0, 1, 0, 0, 1}) {
198 connect_and_test(addresses[i]);
199 }
200}
201
204{
205 IpcPipeTest();
207 IpcSocketTest(m_args.GetDataDirNet());
208}
209
210// Test address parsing.
211BOOST_AUTO_TEST_CASE(parse_address_test)
212{
213 std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
214 fs::path datadir{"/var/empty/notexist"};
215 auto check_notexist{[](const std::system_error& e) { return e.code() == std::errc::no_such_file_or_directory; }};
216 auto check_address{[&](std::string address, std::string expect_address, std::string expect_error) {
217 if (expect_error.empty()) {
218 BOOST_CHECK_EXCEPTION(process->connect(datadir, "test_bitcoin", address), std::system_error, check_notexist);
219 } else {
220 BOOST_CHECK_EXCEPTION(process->connect(datadir, "test_bitcoin", address), std::invalid_argument, HasReason(expect_error));
221 }
222 BOOST_CHECK_EQUAL(address, expect_address);
223 }};
224 std::string prefix{fs::PathToString(datadir / "")};
225 check_address("unix", "unix:" + prefix + "test_bitcoin.sock", "");
226 check_address("unix:", "unix:" + prefix + "test_bitcoin.sock", "");
227 check_address("unix:path.sock", "unix:" + prefix + "path.sock", "");
228 check_address("unix:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.sock",
229 "unix:" + prefix + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.sock",
230 "Unix address path \"" + prefix + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.sock\" exceeded maximum socket path length");
231 check_address("invalid", "invalid", "Unrecognized address 'invalid'");
232}
233
234// Test command line parsing in ipc::Process::checkSpawned().
235BOOST_AUTO_TEST_CASE(check_spawned_test)
236{
237 std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
238 char arg0[]{"bitcoin-node"};
239 char arg_spawn[]{"-ipcchild"};
240 char arg_invalid[]{"invalid"};
241 char arg_other[]{"-ipcbind=unix"};
242
243 // no -ipcchild arg.
244 {
245 char* argv[]{arg0};
246 BOOST_CHECK(!process->checkSpawned(1, argv));
247 }
248 {
249 char* argv[]{arg0, arg_other};
250 BOOST_CHECK(!process->checkSpawned(2, argv));
251 }
252 {
253 char* argv[]{arg0, arg_other, arg_invalid};
254 BOOST_CHECK(!process->checkSpawned(3, argv));
255 }
256 // -ipcchild without a value.
257 {
258 char* argv[]{arg0, arg_spawn};
259 BOOST_CHECK(!process->checkSpawned(2, argv));
260 }
261 // -ipcchild combined with other arguments.
262 {
263 char* argv[]{arg0, arg_spawn, arg_invalid, arg_other};
264 BOOST_CHECK(!process->checkSpawned(4, argv));
265 }
266 // -ipcchild with a value that is not a valid way of connecting to the
267 // parent process.
268 {
269 char* argv[]{arg0, arg_spawn, arg_invalid};
270 BOOST_CHECK_THROW(process->checkSpawned(3, argv), std::runtime_error);
271 }
272}
273
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:116
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:37
std::unique_ptr< interfaces::Echo > makeEcho() override
Definition: ipc_tests.cpp:39
@ VOBJ
Definition: univalue.h:24
Initial interface created when a process is first started, and used to give and get access to other i...
Definition: init.h:39
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_EQUAL(headers.FindFirst("key"), "value")
BOOST_CHECK_EXCEPTION(HTTPHeaders{}.Read(reader), std::runtime_error, HasReason{"Empty HTTP header name"})
BOOST_AUTO_TEST_CASE(ipc_tests)
Definition: ipc_tests.cpp:203
static std::string TempPath(std::string_view pattern)
Generate a temporary path with temp_directory_path and mkstemp.
Definition: ipc_tests.cpp:43
void IpcSocketTest(const fs::path &datadir)
Test ipc::Process bind() and connect() methods connecting over a unix socket.
Definition: ipc_tests.cpp:155
void IpcPipeTest()
Unit test that tests execution of IPC calls without actually creating a separate process.
Definition: ipc_tests.cpp:63
void IpcSocketPairTest()
Test ipc::Protocol connect() and serve() methods connecting over a socketpair.
Definition: ipc_tests.cpp:131
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:125
Definition: basic.cpp:11
std::unique_ptr< Echo > MakeEcho()
Return implementation of Echo interface.
Definition: interfaces.cpp:53
std::unique_ptr< Protocol > MakeCapnpProtocol(const char *exe_name)
Definition: protocol.cpp:160
std::unique_ptr< Process > MakeProcess()
Constructor for Process interface.
Definition: process.cpp:157
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
#define BOOST_CHECK_THROW(stmt, excMatch)
Definition: object.cpp:18
#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)
Definition: transaction.h:418
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:417
const char * prefix
Definition: rest.cpp:1197
@ OP_11
Definition: script.h:95
Basic testing setup.
Definition: setup_common.h:58
A mutable version of CTransaction.
Definition: transaction.h:372
std::vector< CTxOut > vout
Definition: transaction.h:374
std::vector< CTxIn > vin
Definition: transaction.h:373
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...
Definition: tinyformat.h:1172