Bitcoin Core  29.1.0
P2P Digital Currency
ipc_test.cpp
Go to the documentation of this file.
1 // Copyright (c) 2023 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/protocol.h>
7 #include <ipc/process.h>
8 #include <ipc/protocol.h>
9 #include <logging.h>
10 #include <mp/proxy-types.h>
11 #include <test/ipc_test.capnp.h>
12 #include <test/ipc_test.capnp.proxy.h>
13 #include <test/ipc_test.h>
14 #include <tinyformat.h>
15 #include <validation.h>
16 
17 #include <future>
18 #include <thread>
19 #include <kj/common.h>
20 #include <kj/memory.h>
21 #include <kj/test.h>
22 #include <stdexcept>
23 
24 #include <boost/test/unit_test.hpp>
25 
27 class TestInit : public interfaces::Init
28 {
29 public:
30  std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); }
31 };
32 
34 static std::string TempPath(std::string_view pattern)
35 {
36  std::string temp{fs::PathToString(fs::path{fs::temp_directory_path()} / fs::PathFromString(std::string{pattern}))};
37  temp.push_back('\0');
38  int fd{mkstemp(temp.data())};
39  BOOST_CHECK_GE(fd, 0);
40  BOOST_CHECK_EQUAL(close(fd), 0);
41  temp.resize(temp.size() - 1);
42  fs::remove(fs::PathFromString(temp));
43  return temp;
44 }
45 
55 {
56  // Setup: create FooImplemention object and listen for FooInterface requests
57  std::promise<std::unique_ptr<mp::ProxyClient<gen::FooInterface>>> foo_promise;
58  std::function<void()> disconnect_client;
59  std::thread thread([&]() {
60  mp::EventLoop loop("IpcPipeTest", [](bool raise, const std::string& log) { LogPrintf("LOG%i: %s\n", raise, log); });
61  auto pipe = loop.m_io_context.provider->newTwoWayPipe();
62 
63  auto connection_client = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[0]));
64  auto foo_client = std::make_unique<mp::ProxyClient<gen::FooInterface>>(
65  connection_client->m_rpc_system->bootstrap(mp::ServerVatId().vat_id).castAs<gen::FooInterface>(),
66  connection_client.get(), /* destroy_connection= */ false);
67  foo_promise.set_value(std::move(foo_client));
68  disconnect_client = [&] { loop.sync([&] { connection_client.reset(); }); };
69 
70  auto connection_server = std::make_unique<mp::Connection>(loop, kj::mv(pipe.ends[1]), [&](mp::Connection& connection) {
71  auto foo_server = kj::heap<mp::ProxyServer<gen::FooInterface>>(std::make_shared<FooImplementation>(), connection);
72  return capnp::Capability::Client(kj::mv(foo_server));
73  });
74  connection_server->onDisconnect([&] { connection_server.reset(); });
75  loop.loop();
76  });
77  std::unique_ptr<mp::ProxyClient<gen::FooInterface>> foo{foo_promise.get_future().get()};
78 
79  // Test: make sure arguments were sent and return value is received
80  BOOST_CHECK_EQUAL(foo->add(1, 2), 3);
81 
82  COutPoint txout1{Txid::FromUint256(uint256{100}), 200};
83  COutPoint txout2{foo->passOutPoint(txout1)};
84  BOOST_CHECK(txout1 == txout2);
85 
87  uni1.pushKV("i", 1);
88  uni1.pushKV("s", "two");
89  UniValue uni2{foo->passUniValue(uni1)};
90  BOOST_CHECK_EQUAL(uni1.write(), uni2.write());
91 
93  mtx.version = 2;
94  mtx.nLockTime = 3;
95  mtx.vin.emplace_back(txout1);
96  mtx.vout.emplace_back(COIN, CScript());
98  CTransactionRef tx2{foo->passTransaction(tx1)};
99  BOOST_CHECK(*Assert(tx1) == *Assert(tx2));
100 
101  std::vector<char> vec1{'H', 'e', 'l', 'l', 'o'};
102  std::vector<char> vec2{foo->passVectorChar(vec1)};
103  BOOST_CHECK_EQUAL(std::string_view(vec1.begin(), vec1.end()), std::string_view(vec2.begin(), vec2.end()));
104 
106  bs1.Invalid(BlockValidationResult::BLOCK_CHECKPOINT, "reject reason", "debug message");
107  BlockValidationState bs2{foo->passBlockState(bs1)};
108  BOOST_CHECK_EQUAL(bs1.IsValid(), bs2.IsValid());
109  BOOST_CHECK_EQUAL(bs1.IsError(), bs2.IsError());
110  BOOST_CHECK_EQUAL(bs1.IsInvalid(), bs2.IsInvalid());
111  BOOST_CHECK_EQUAL(static_cast<int>(bs1.GetResult()), static_cast<int>(bs2.GetResult()));
112  BOOST_CHECK_EQUAL(bs1.GetRejectReason(), bs2.GetRejectReason());
113  BOOST_CHECK_EQUAL(bs1.GetDebugMessage(), bs2.GetDebugMessage());
114 
116  BlockValidationState bs4{foo->passBlockState(bs3)};
117  BOOST_CHECK_EQUAL(bs3.IsValid(), bs4.IsValid());
118  BOOST_CHECK_EQUAL(bs3.IsError(), bs4.IsError());
119  BOOST_CHECK_EQUAL(bs3.IsInvalid(), bs4.IsInvalid());
120  BOOST_CHECK_EQUAL(static_cast<int>(bs3.GetResult()), static_cast<int>(bs4.GetResult()));
121  BOOST_CHECK_EQUAL(bs3.GetRejectReason(), bs4.GetRejectReason());
122  BOOST_CHECK_EQUAL(bs3.GetDebugMessage(), bs4.GetDebugMessage());
123 
124  auto script1{CScript() << OP_11};
125  auto script2{foo->passScript(script1)};
126  BOOST_CHECK_EQUAL(HexStr(script1), HexStr(script2));
127 
128  // Test cleanup: disconnect pipe and join thread
129  disconnect_client();
130  thread.join();
131 }
132 
135 {
136  int fds[2];
137  BOOST_CHECK_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, fds), 0);
138  std::unique_ptr<interfaces::Init> init{std::make_unique<TestInit>()};
139  std::unique_ptr<ipc::Protocol> protocol{ipc::capnp::MakeCapnpProtocol()};
140  std::promise<void> promise;
141  std::thread thread([&]() {
142  protocol->serve(fds[0], "test-serve", *init, [&] { promise.set_value(); });
143  });
144  promise.get_future().wait();
145  std::unique_ptr<interfaces::Init> remote_init{protocol->connect(fds[1], "test-connect")};
146  std::unique_ptr<interfaces::Echo> remote_echo{remote_init->makeEcho()};
147  BOOST_CHECK_EQUAL(remote_echo->echo("echo test"), "echo test");
148  remote_echo.reset();
149  remote_init.reset();
150  thread.join();
151 }
152 
154 void IpcSocketTest(const fs::path& datadir)
155 {
156  std::unique_ptr<interfaces::Init> init{std::make_unique<TestInit>()};
157  std::unique_ptr<ipc::Protocol> protocol{ipc::capnp::MakeCapnpProtocol()};
158  std::unique_ptr<ipc::Process> process{ipc::MakeProcess()};
159 
160  std::string invalid_bind{"invalid:"};
161  BOOST_CHECK_THROW(process->bind(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
162  BOOST_CHECK_THROW(process->connect(datadir, "test_bitcoin", invalid_bind), std::invalid_argument);
163 
164  auto bind_and_listen{[&](const std::string& bind_address) {
165  std::string address{bind_address};
166  int serve_fd = process->bind(datadir, "test_bitcoin", address);
167  BOOST_CHECK_GE(serve_fd, 0);
168  BOOST_CHECK_EQUAL(address, bind_address);
169  protocol->listen(serve_fd, "test-serve", *init);
170  }};
171 
172  auto connect_and_test{[&](const std::string& connect_address) {
173  std::string address{connect_address};
174  int connect_fd{process->connect(datadir, "test_bitcoin", address)};
175  BOOST_CHECK_EQUAL(address, connect_address);
176  std::unique_ptr<interfaces::Init> remote_init{protocol->connect(connect_fd, "test-connect")};
177  std::unique_ptr<interfaces::Echo> remote_echo{remote_init->makeEcho()};
178  BOOST_CHECK_EQUAL(remote_echo->echo("echo test"), "echo test");
179  }};
180 
181  // Need to specify explicit socket addresses outside the data directory, because the data
182  // directory path is so long that the default socket address and any other
183  // addresses in the data directory would fail with errors like:
184  // Address 'unix' path '"/tmp/test_common_Bitcoin Core/ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff/test_bitcoin.sock"' exceeded maximum socket path length
185  std::vector<std::string> addresses{
186  strprintf("unix:%s", TempPath("bitcoin_sock0_XXXXXX")),
187  strprintf("unix:%s", TempPath("bitcoin_sock1_XXXXXX")),
188  };
189 
190  // Bind and listen on multiple addresses
191  for (const auto& address : addresses) {
192  bind_and_listen(address);
193  }
194 
195  // Connect and test each address multiple times.
196  for (int i : {0, 1, 0, 0, 1}) {
197  connect_and_test(addresses[i]);
198  }
199 }
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
static path PathFromString(const std::string &string)
Convert byte string to path object.
Definition: fs.h:174
void IpcSocketTest(const fs::path &datadir)
Test ipc::Process bind() and connect() methods connecting over a unix socket.
Definition: ipc_test.cpp:154
std::string GetDebugMessage() const
Definition: validation.h:111
#define BOOST_CHECK_THROW(stmt, excMatch)
Definition: object.cpp:19
std::unique_ptr< Echo > MakeEcho()
Return implementation of Echo interface.
Definition: interfaces.cpp:52
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
bool IsValid() const
Definition: validation.h:106
Definition: script.h:94
static std::string PathToString(const path &path)
Convert path object to a byte string.
Definition: fs.h:151
bool Invalid(Result result, const std::string &reject_reason="", const std::string &debug_message="")
Definition: validation.h:89
the block failed to meet one of our checkpoints
void IpcPipeTest()
Unit test that tests execution of IPC calls without actually creating a separate process.
Definition: ipc_test.cpp:54
static std::string TempPath(std::string_view pattern)
Generate a temporary path with temp_directory_path and mkstemp.
Definition: ipc_test.cpp:34
Result GetResult() const
Definition: validation.h:109
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:28
std::unique_ptr< Protocol > MakeCapnpProtocol()
Definition: protocol.cpp:102
std::unique_ptr< interfaces::Echo > makeEcho() override
Definition: ipc_test.cpp:30
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
256-bit opaque blob.
Definition: uint256.h:201
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:414
static transaction_identifier FromUint256(const uint256 &id)
Remote init class.
Definition: ipc_test.cpp:27
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:29
bool IsInvalid() const
Definition: validation.h:107
A mutable version of CTransaction.
Definition: transaction.h:377
Initial interface created when a process is first started, and used to give and get access to other i...
Definition: init.h:30
std::string GetRejectReason() const
Definition: validation.h:110
#define LogPrintf(...)
Definition: logging.h:361
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:32
std::unique_ptr< Process > MakeProcess()
Constructor for Process interface.
Definition: process.cpp:154
#define Assert(val)
Identity function.
Definition: check.h:85
void IpcSocketPairTest()
Test ipc::Protocol connect() and serve() methods connecting over a socketpair.
Definition: ipc_test.cpp:134
bool IsError() const
Definition: validation.h:108
#define BOOST_CHECK(expr)
Definition: object.cpp:17
static constexpr CAmount COIN
The amount of satoshis in one BTC.
Definition: amount.h:15