Bitcoin Core 31.99.0
P2P Digital Currency
test.cpp
Go to the documentation of this file.
1// Copyright (c) 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 <mp/test/foo.capnp.h>
6#include <mp/test/foo.capnp.proxy.h>
7
8#include <atomic>
9#include <capnp/capability.h>
10#include <capnp/rpc.h>
11#include <cassert>
12#include <chrono>
13#include <condition_variable>
14#include <cstdint>
15#include <cstring>
16#include <functional>
17#include <future>
18#include <kj/async.h>
19#include <kj/async-io.h>
20#include <kj/common.h>
21#include <kj/exception.h>
22#include <kj/debug.h>
23#include <kj/memory.h>
24#include <kj/string.h>
25#include <kj/test.h>
26#include <map>
27#include <memory>
28#include <mp/proxy.h>
29#include <mp/proxy.capnp.h>
30#include <mp/proxy-io.h>
31#include <mp/util.h>
32#include <mp/version.h>
33#include <optional>
34#include <set>
35#include <stdexcept>
36#include <string>
37#include <string_view>
38#include <thread>
39#include <type_traits>
40#include <unordered_set>
41#include <utility>
42#include <vector>
43
45#define EXPECT_EXCEPTION(call, message) \
46 try { \
47 call; \
48 KJ_EXPECT(false); \
49 } catch (const std::runtime_error& e) { \
50 KJ_EXPECT(std::string_view{e.what()} == message); \
51 }
52
53namespace mp {
54namespace test {
55
59static_assert(std::is_integral_v<decltype(kMP_MAJOR_VERSION)>, "MP_MAJOR_VERSION must be an integral constant");
60static_assert(std::is_integral_v<decltype(kMP_MINOR_VERSION)>, "MP_MINOR_VERSION must be an integral constant");
61
77{
78public:
79 std::function<void()> server_disconnect;
80 std::function<void()> server_disconnect_later;
81 std::function<void()> client_disconnect;
82 std::promise<std::unique_ptr<ProxyClient<messages::FooInterface>>> client_promise;
83 std::unique_ptr<ProxyClient<messages::FooInterface>> client;
88
89 TestSetup(bool client_owns_connection = true)
90 : thread{[&] {
91 EventLoop loop("mptest", [](mp::LogMessage log) {
92 // Info logs are not printed by default, but will be shown with `mptest --verbose`
93 KJ_LOG(INFO, log.level, log.message);
94 if (log.level == mp::Log::Raise) throw std::runtime_error(log.message);
95 });
96 auto pipe = loop.m_io_context.provider->newTwoWayPipe();
97
98 auto server_connection =
99 std::make_unique<Connection>(loop, kj::mv(pipe.ends[0]), [&](Connection& connection) {
100 auto server_proxy = kj::heap<ProxyServer<messages::FooInterface>>(
101 std::make_shared<FooImplementation>(), connection);
102 server = server_proxy;
103 return capnp::Capability::Client(kj::mv(server_proxy));
104 });
105 server_disconnect = [&] { loop.sync([&] { server_connection.reset(); }); };
107 assert(std::this_thread::get_id() == loop.m_thread_id);
108 loop.m_task_set->add(kj::evalLater([&] { server_connection.reset(); }));
109 };
110 // Set handler to destroy the server when the client disconnects. This
111 // is ignored if server_disconnect() is called instead.
112 server_connection->onDisconnect([&] { server_connection.reset(); });
113
114 auto client_connection = std::make_unique<Connection>(loop, kj::mv(pipe.ends[1]));
115 auto client_proxy = std::make_unique<ProxyClient<messages::FooInterface>>(
116 client_connection->m_rpc_system->bootstrap(ServerVatId().vat_id).castAs<messages::FooInterface>(),
117 client_connection.get(), /* destroy_connection= */ client_owns_connection);
118 if (client_owns_connection) {
119 (void)client_connection.release();
120 } else {
121 client_disconnect = [&] { loop.sync([&] { client_connection.reset(); }); };
122 }
123
124 client_promise.set_value(std::move(client_proxy));
125 loop.loop();
126 }}
127 {
128 client = client_promise.get_future().get();
129 }
130
132 {
133 // Test that client cleanup_fns are executed.
134 bool destroyed = false;
135 client->m_context.cleanup_fns.emplace_front([&destroyed] { destroyed = true; });
136 client.reset();
137 KJ_EXPECT(destroyed);
138
139 thread.join();
140 }
141};
142
143KJ_TEST("Call FooInterface methods")
144{
146 ProxyClient<messages::FooInterface>* foo = setup.client.get();
147
148 KJ_EXPECT(foo->add(1, 2) == 3);
149 int ret;
150 foo->addOut(3, 4, ret);
151 KJ_EXPECT(ret == 7);
152 foo->addInOut(3, ret);
153 KJ_EXPECT(ret == 10);
154
155 FooStruct in;
156 in.name = "name";
157 in.set_int.insert(2);
158 in.set_int.insert(1);
159 in.unordered_set_int.insert(2);
160 in.unordered_set_int.insert(1);
161 in.vector_bool.push_back(false);
162 in.vector_bool.push_back(true);
163 in.vector_bool.push_back(false);
164 in.optional_int = 3;
165 in.map_string_int.emplace("a", 1);
166 in.map_string_int.emplace("b", 2);
167 FooStruct out = foo->pass(in);
168 KJ_EXPECT(in.name == out.name);
169 KJ_EXPECT(in.set_int.size() == out.set_int.size());
170 for (auto init{in.set_int.begin()}, outit{out.set_int.begin()}; init != in.set_int.end() && outit != out.set_int.end(); ++init, ++outit) {
171 KJ_EXPECT(*init == *outit);
172 }
173 KJ_EXPECT(in.unordered_set_int.size() == out.unordered_set_int.size());
174 for (const auto& elem : in.unordered_set_int) {
175 KJ_EXPECT(out.unordered_set_int.count(elem) == 1);
176 }
177 KJ_EXPECT(in.vector_bool.size() == out.vector_bool.size());
178 for (size_t i = 0; i < in.vector_bool.size(); ++i) {
179 KJ_EXPECT(in.vector_bool[i] == out.vector_bool[i]);
180 }
181 KJ_EXPECT(in.optional_int == out.optional_int);
182 KJ_EXPECT(in.map_string_int.size() == out.map_string_int.size());
183 for (auto init{in.map_string_int.begin()}, outit{out.map_string_int.begin()}; init != in.map_string_int.end() && outit != out.map_string_int.end(); ++init, ++outit) {
184 KJ_EXPECT(init->first == outit->first);
185 KJ_EXPECT(init->second == outit->second);
186 }
187
188 // Additional checks for std::optional member
189 KJ_EXPECT(foo->pass(in).optional_int == 3);
190 in.optional_int.reset();
191 KJ_EXPECT(!foo->pass(in).optional_int);
192
193 FooStruct err;
194 try {
195 foo->raise(in);
196 } catch (const FooStruct& e) {
197 err = e;
198 }
199 KJ_EXPECT(in.name == err.name);
200
201 class Callback : public ExtendedCallback
202 {
203 public:
204 Callback(int expect, int ret) : m_expect(expect), m_ret(ret) {}
205 int call(int arg) override
206 {
207 KJ_EXPECT(arg == m_expect);
208 return m_ret;
209 }
210 int callExtended(int arg) override
211 {
212 KJ_EXPECT(arg == m_expect + 10);
213 return m_ret + 10;
214 }
215 int m_expect, m_ret;
216 };
217
218 foo->initThreadMap();
219 Callback callback(1, 2);
220 KJ_EXPECT(foo->callback(callback, 1) == 2);
221 KJ_EXPECT(foo->callbackUnique(std::make_unique<Callback>(3, 4), 3) == 4);
222 KJ_EXPECT(foo->callbackShared(std::make_shared<Callback>(5, 6), 5) == 6);
223 auto saved = std::make_shared<Callback>(7, 8);
224 KJ_EXPECT(saved.use_count() == 1);
225 foo->saveCallback(saved);
226 KJ_EXPECT(saved.use_count() == 2);
227 foo->callbackSaved(7);
228 KJ_EXPECT(foo->callbackSaved(7) == 8);
229 foo->saveCallback(nullptr);
230 KJ_EXPECT(saved.use_count() == 1);
231 KJ_EXPECT(foo->callbackExtended(callback, 11) == 12);
232
233 FooCustom custom_in;
234 custom_in.v1 = "v1";
235 custom_in.v2 = 5;
236 FooCustom custom_out = foo->passCustom(custom_in);
237 KJ_EXPECT(custom_in.v1 == custom_out.v1);
238 KJ_EXPECT(custom_in.v2 == custom_out.v2);
239
240 foo->passEmpty(FooEmpty{});
241
242 FooData empty_data_out = foo->passData(FooData{});
243 KJ_EXPECT(empty_data_out.empty());
244
245 FooMessage message1;
246 message1.message = "init";
247 FooMessage message2{foo->passMessage(message1)};
248 KJ_EXPECT(message2.message == "init build read call build read");
249
250 FooMutable mut;
251 mut.message = "init";
252 foo->passMutable(mut);
253 KJ_EXPECT(mut.message == "init build pass call return read");
254
255 KJ_EXPECT(foo->passDouble(1.25) == 1.25);
256
257 KJ_EXPECT(foo->passFn([]{ return 10; }) == 10);
258
259 // Recursive async IPC calls
260 KJ_EXPECT(foo->passFn([foo]{
261 return foo->passFn([]{ return 1; });
262 }) == 1);
263
264 std::vector<FooDataRef> data_in;
265 data_in.push_back(std::make_shared<FooData>(FooData{'H', 'i'}));
266 data_in.push_back(nullptr);
267 std::vector<FooDataRef> data_out{foo->passDataPointers(data_in)};
268 KJ_EXPECT(data_out.size() == 2);
269 KJ_REQUIRE(data_out[0] != nullptr);
270 KJ_EXPECT(*data_out[0] == *data_in[0]);
271 KJ_EXPECT(!data_out[1]);
272}
273
274KJ_TEST("Call IPC method after client connection is closed")
275{
276 TestSetup setup{/*client_owns_connection=*/false};
277 ProxyClient<messages::FooInterface>* foo = setup.client.get();
278 KJ_EXPECT(foo->add(1, 2) == 3);
279 setup.client_disconnect();
280
281 EXPECT_EXCEPTION(foo->add(1, 2), "IPC client method called after disconnect.");
282}
283
284KJ_TEST("Calling IPC method after server connection is closed")
285{
287 ProxyClient<messages::FooInterface>* foo = setup.client.get();
288 KJ_EXPECT(foo->add(1, 2) == 3);
289 setup.server_disconnect();
290
291 EXPECT_EXCEPTION(foo->add(1, 2), "IPC client method call interrupted by disconnect.");
292}
293
294KJ_TEST("Calling IPC method and disconnecting during the call")
295{
296 TestSetup setup{/*client_owns_connection=*/false};
297 ProxyClient<messages::FooInterface>* foo = setup.client.get();
298 KJ_EXPECT(foo->add(1, 2) == 3);
299
300 // Set m_fn to initiate client disconnect when server is in the middle of
301 // handling the callFn call to make sure this case is handled cleanly.
302 setup.server->m_impl->m_fn = setup.client_disconnect;
303
304 EXPECT_EXCEPTION(foo->callFn(), "IPC client method call interrupted by disconnect.");
305}
306
307KJ_TEST("Calling IPC method, disconnecting and blocking during the call")
308{
309 // This test is similar to last test, except that instead of letting the IPC
310 // call return immediately after triggering a disconnect, make it disconnect
311 // & wait so server is forced to deal with having a disconnection and call
312 // in flight at the same time.
313 //
314 // Test uses callFnAsync() instead of callFn() to implement this. Both of
315 // these methods have the same implementation, but the callFnAsync() capnp
316 // method declaration takes an mp.Context argument so the method executes on
317 // an asynchronous thread instead of executing in the event loop thread, so
318 // it is able to block without deadlocking the event lock thread.
319 //
320 // This test adds important coverage because it causes the server Connection
321 // object to be destroyed before ProxyServer object, which is not a
322 // condition that usually happens because the m_rpc_system.reset() call in
323 // the ~Connection destructor usually would immediately free all remaining
324 // ProxyServer objects associated with the connection. Having an in-progress
325 // RPC call requires keeping the ProxyServer longer.
326
327 std::promise<void> signal;
328 TestSetup setup{/*client_owns_connection=*/false};
329 ProxyClient<messages::FooInterface>* foo = setup.client.get();
330 KJ_EXPECT(foo->add(1, 2) == 3);
331
332 foo->initThreadMap();
333 setup.server->m_impl->m_fn = [&] {
334 EventLoopRef loop{*setup.server->m_context.loop};
335 setup.client_disconnect();
336 signal.get_future().get();
337 };
338
339 EXPECT_EXCEPTION(foo->callFnAsync(), "IPC client method call interrupted by disconnect.");
340
341 // Now that the disconnect has been detected, set signal allowing the
342 // callFnAsync() IPC call to return. Since signalling may not wake up the
343 // thread right away, it is important for the signal variable to be declared
344 // *before* the TestSetup variable so is not destroyed while
345 // signal.get_future().get() is called.
346 signal.set_value();
347}
348
349KJ_TEST("Worker thread destroyed before it is initialized")
350{
351 // Regression test for bitcoin/bitcoin#34711, bitcoin/bitcoin#34756 where a
352 // worker thread is destroyed before it starts waiting for work.
353 //
354 // The test uses the `makethread` hook to trigger a disconnect as soon as
355 // ProxyServer<ThreadMap>::makeThread is called, so without the bugfix,
356 // ProxyServer<Thread>::~ProxyServer would run and destroy the waiter before
357 // the worker thread started waiting, causing a SIGSEGV when it did start.
359 ProxyClient<messages::FooInterface>* foo = setup.client.get();
360 foo->initThreadMap();
361 setup.server->m_impl->m_fn = [] {};
362
363 EventLoop& loop = *setup.server->m_context.connection->m_loop;
364 loop.testing_hook_makethread = [&] {
365 // Use disconnect_later to queue the disconnect, because the makethread
366 // hook is called on the event loop thread. The disconnect should happen
367 // as soon as the event loop is idle.
368 setup.server_disconnect_later();
369 };
371 // Sleep to allow event loop to run and process the queued disconnect
372 // before the worker thread starts waiting.
373 std::this_thread::sleep_for(std::chrono::milliseconds(10));
374 };
375
376 EXPECT_EXCEPTION(foo->callFnAsync(), "IPC client method call interrupted by disconnect.");
377}
378
379KJ_TEST("Calling async IPC method, with server disconnect racing the call")
380{
381 // Regression test for bitcoin/bitcoin#34777 heap-use-after-free where
382 // an async request is canceled before it starts to execute.
383 //
384 // Use testing_hook_async_request_start to trigger a disconnect from the
385 // worker thread as soon as it begins to execute an async request. Without
386 // the bugfix, the worker thread would trigger a SIGSEGV after this by
387 // calling call_context.getParams().
389 ProxyClient<messages::FooInterface>* foo = setup.client.get();
390 foo->initThreadMap();
391 setup.server->m_impl->m_fn = [] {};
392
393 EventLoop& loop = *setup.server->m_context.connection->m_loop;
395 setup.server_disconnect();
396 // Sleep is necessary to let the event loop fully clean up after the
397 // disconnect and trigger the SIGSEGV.
398 std::this_thread::sleep_for(std::chrono::milliseconds(10));
399 };
400
401 EXPECT_EXCEPTION(foo->callFnAsync(), "IPC client method call interrupted by disconnect.");
402}
403
404KJ_TEST("Calling async IPC method, with server disconnect after cleanup")
405{
406 // Regression test for bitcoin/bitcoin#34782 stack-use-after-return where
407 // an async request is canceled after it finishes executing but before the
408 // response is sent.
409 //
410 // Use testing_hook_async_request_done to trigger a disconnect from the
411 // worker thread after it executes an async request but before it returns.
412 // Without the bugfix, the m_on_cancel callback would be called at this
413 // point, accessing the cancel_mutex stack variable that had gone out of
414 // scope.
416 ProxyClient<messages::FooInterface>* foo = setup.client.get();
417 foo->initThreadMap();
418 setup.server->m_impl->m_fn = [] {};
419
420 EventLoop& loop = *setup.server->m_context.connection->m_loop;
422 setup.server_disconnect();
423 };
424
425 EXPECT_EXCEPTION(foo->callFnAsync(), "IPC client method call interrupted by disconnect.");
426}
427
428KJ_TEST("Destroying ProxyClient<> with destroy method after peer disconnect")
429{
430 // Regression test for bitcoin-core/libmultiprocess#219 where
431 // ~ProxyClientBase would call std::terminate if the remote destroy RPC
432 // failed during teardown.
433 //
434 // Save a callback on the server so it holds a ProxyClient<FooCallback>
435 // pointing back to this side, then disconnect. When the server is torn
436 // down, the ProxyClient<FooCallback> destructor issues a destroy RPC over
437 // the now dead connection; without the bugfix the exception escapes the
438 // noexcept destructor and aborts the process.
439
440 TestSetup setup{/*client_owns_connection=*/false};
441 ProxyClient<messages::FooInterface>* foo = setup.client.get();
442 foo->initThreadMap();
443
444 class Callback : public FooCallback
445 {
446 public:
447 int call(int arg) override { return arg; }
448 };
449
450 foo->saveCallback(std::make_shared<Callback>());
451 setup.client_disconnect();
452}
453
454KJ_TEST("Make simultaneous IPC calls on single remote thread")
455{
457 ProxyClient<messages::FooInterface>* foo = setup.client.get();
458 std::promise<void> signal;
459
460 foo->initThreadMap();
461 // Use callFnAsync() to get the client to set up the request_thread
462 // that will be used for the test.
463 setup.server->m_impl->m_fn = [&] {};
464 foo->callFnAsync();
466 Thread::Client *callback_thread, *request_thread;
467 foo->m_context.loop->sync([&] {
468 Lock lock(tc.waiter->m_mutex);
469 callback_thread = &tc.callback_threads.at(foo->m_context.connection)->m_client;
470 request_thread = &tc.request_threads.at(foo->m_context.connection)->m_client;
471 });
472
473 // Call callIntFnAsync 3 times with n=100, 200, 300
474 std::atomic<int> expected = 100;
475
476 setup.server->m_impl->m_int_fn = [&](int n) {
477 assert(n == expected);
478 expected += 100;
479 return n;
480 };
481
482 auto client{foo->m_client};
483 std::atomic<size_t> running{3};
484 foo->m_context.loop->sync([&]
485 {
486 for (size_t i = 0; i < running; i++)
487 {
488 auto request{client.callIntFnAsyncRequest()};
489 auto context{request.initContext()};
490 context.setCallbackThread(*callback_thread);
491 context.setThread(*request_thread);
492 request.setArg(100 * (i+1));
493 foo->m_context.loop->m_task_set->add(request.send().then(
494 [&running, &tc, i](auto&& results) {
495 assert(results.getResult() == static_cast<int32_t>(100 * (i+1)));
496 running -= 1;
497 Lock lock(tc.waiter->m_mutex);
498 tc.waiter->m_cv.notify_all();
499 }));
500 }
501 });
502 {
503 Lock lock(tc.waiter->m_mutex);
504 tc.waiter->wait(lock, [&running] { return running == 0; });
505 }
506 KJ_EXPECT(expected == 400);
507}
508
509KJ_TEST("Call async IPC method dispatched to pool thread")
510{
512 ProxyClient<messages::FooInterface>* foo = setup.client.get();
513
514 // Set up the thread map exchange so the client has the server's ThreadMap,
515 // then call makePool to pre-allocate two server threads.
516 foo->initThreadMap();
517 setup.server->m_impl->m_int_fn = [](int n) { return n * 2; };
518
520 std::atomic<size_t> running{3};
521 std::promise<void> pool_ready;
522 foo->m_context.loop->sync([&] {
523 auto pool_req = foo->m_context.connection->m_thread_map.makePoolRequest();
524 pool_req.setCount(2);
525 foo->m_context.loop->m_task_set->add(
526 pool_req.send().then([&](auto&&) { pool_ready.set_value(); }));
527 });
528 pool_ready.get_future().get();
529
530 // Send three callIntFnAsync requests with no context.thread set.
531 // The server should dispatch each to a pool thread.
532 auto client{foo->m_client};
533 foo->m_context.loop->sync([&] {
534 for (size_t i = 0; i < running; ++i) {
535 auto request{client.callIntFnAsyncRequest()};
536 request.initContext(); // context present but thread unset
537 request.setArg(static_cast<int32_t>(i + 1));
538 foo->m_context.loop->m_task_set->add(request.send().then(
539 [&running, &tc, i](auto&& results) {
540 assert(results.getResult() == static_cast<int32_t>((i + 1) * 2));
541 running -= 1;
542 Lock lock(tc.waiter->m_mutex);
543 tc.waiter->m_cv.notify_all();
544 }));
545 }
546 });
547 {
548 Lock lock(tc.waiter->m_mutex);
549 tc.waiter->wait(lock, [&running] { return running == 0; });
550 }
551}
552
553KJ_TEST("Call async IPC method without thread or pool errors correctly")
554{
556 ProxyClient<messages::FooInterface>* foo = setup.client.get();
557 setup.server->m_impl->m_fn = [] {};
558
559 // Send a callFnAsync request with no context.thread and no pool configured.
560 // The server should throw the "no thread specified and no pool configured" error.
561 std::promise<void> done;
562 bool error_thrown{false};
563 foo->m_context.loop->sync([&] {
564 auto request{foo->m_client.callFnAsyncRequest()};
565 request.initContext();
566 foo->m_context.loop->m_task_set->add(
567 request.send().then(
568 [&](auto&&) { done.set_value(); },
569 [&](kj::Exception&& e) {
570 error_thrown = true;
571 KJ_EXPECT(std::string_view{e.getDescription().cStr()}.find(
572 "no thread specified and no pool configured") != std::string_view::npos);
573 done.set_value();
574 }));
575 });
576 done.get_future().get();
577 KJ_EXPECT(error_thrown);
578}
579
580} // namespace test
581} // namespace mp
int ret
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
std::function< void()> testing_hook_makethread
Hook called when ProxyServer<ThreadMap>::makeThread() is called.
Definition: proxy-io.h:352
std::function< void()> testing_hook_makethread_created
Hook called on the worker thread inside makeThread(), after the thread context is set up and thread_c...
Definition: proxy-io.h:357
std::function< void()> testing_hook_async_request_done
Hook called on the worker thread just before returning results.
Definition: proxy-io.h:365
std::function< void()> testing_hook_async_request_start
Hook called on the worker thread when it starts to execute an async request.
Definition: proxy-io.h:362
Event loop smart pointer automatically managing m_num_refs.
Definition: proxy.h:51
Definition: util.h:177
Test setup class creating a two way connection between a ProxyServer<FooInterface> object and a Proxy...
Definition: test.cpp:77
std::promise< std::unique_ptr< ProxyClient< messages::FooInterface > > > client_promise
Definition: test.cpp:82
TestSetup(bool client_owns_connection=true)
Definition: test.cpp:89
std::function< void()> server_disconnect
Definition: test.cpp:79
std::function< void()> client_disconnect
Definition: test.cpp:81
std::thread thread
Thread variable should be after other struct members so the thread does not start until the other mem...
Definition: test.cpp:87
std::function< void()> server_disconnect_later
Definition: test.cpp:80
ProxyServer< messages::FooInterface > * server
Definition: test.cpp:84
std::unique_ptr< ProxyClient< messages::FooInterface > > client
Definition: test.cpp:83
#define EXPECT_EXCEPTION(call, message)
Assert that a call throws std::runtime_error with the given message.
Definition: test.cpp:45
std::unique_ptr< ProxyClient< messages::FooInterface > > client
std::promise< std::unique_ptr< ProxyClient< messages::FooInterface > > > client_promise
std::thread thread
Thread variable should be after other struct members so the thread does not start until the other mem...
Definition: basic.cpp:8
std::vector< char > FooData
Definition: foo.h:53
constexpr auto kMP_MAJOR_VERSION
Check version.h header values.
Definition: test.cpp:57
constexpr auto kMP_MINOR_VERSION
Definition: test.cpp:58
Functions to serialize / deserialize common bitcoin types.
Definition: common-types.h:57
thread_local ThreadContext g_thread_context
Definition: proxy.cpp:44
KJ_TEST("SpawnProcess does not run callback in child")
Definition: spawn_tests.cpp:46
Log level
The severity level of this message.
Definition: proxy-io.h:147
std::string message
Message to be logged.
Definition: proxy-io.h:144
Mapping from capnp interface type to proxy client implementation (specializations are generated by pr...
Definition: proxy.h:25
Vat id for server side of connection.
Definition: proxy-io.h:520
The thread_local ThreadContext g_thread_context struct provides information about individual threads ...
Definition: proxy-io.h:706
std::string v1
Definition: foo.h:35
std::string message
Definition: foo.h:45
std::string message
Definition: foo.h:50
std::string name
Definition: foo.h:23
std::map< std::string, int > map_string_int
Definition: foo.h:28
std::unordered_set< int > unordered_set_int
Definition: foo.h:27
std::optional< int > optional_int
Definition: foo.h:26
std::vector< bool > vector_bool
Definition: foo.h:25
std::set< int > set_int
Definition: foo.h:24
static int setup(void)
Definition: tests.c:8056
#define expect(bit)
assert(!tx.IsCoinBase())
Major and minor version numbers.
#define MP_MAJOR_VERSION
Major version number.
Definition: version.h:27
#define MP_MINOR_VERSION
Minor version number.
Definition: version.h:32