// Copyright (c) 2020-2024, The Monero Project // // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_basic.h" #include "cryptonote_basic/events.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "json_serialization.h" #include "net/zmq.h" #include "rpc/message.h" #include "rpc/zmq_pub.h" #include "rpc/zmq_server.h" #include "serialization/json_object.h" #define MASSERT(...) \ if (!(__VA_ARGS__)) \ return testing::AssertionFailure() << BOOST_PP_STRINGIZE(__VA_ARGS__) TEST(ZmqFullMessage, InvalidRequest) { EXPECT_THROW( (cryptonote::rpc::FullMessage{"{\"jsonrpc\":\"2.0\",\"id\":0,\"params\":[]}", true}), cryptonote::json::MISSING_KEY ); EXPECT_THROW( (cryptonote::rpc::FullMessage{"{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":3,\"params\":[]}", true}), cryptonote::json::WRONG_TYPE ); } TEST(ZmqFullMessage, Request) { static constexpr const char request[] = "{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"foo\",\"params\":[]}"; EXPECT_NO_THROW( (cryptonote::rpc::FullMessage{request, true}) ); cryptonote::rpc::FullMessage parsed{request, true}; EXPECT_STREQ("foo", parsed.getRequestType().c_str()); } namespace { using published_json = std::pair; constexpr const char inproc_pub[] = "inproc://dummy_pub"; net::zmq::socket create_socket(void* ctx, const char* address) { net::zmq::socket sock{zmq_socket(ctx, ZMQ_PAIR)}; if (!sock) MONERO_ZMQ_THROW("failed to create socket"); if (zmq_bind(sock.get(), address) != 0) MONERO_ZMQ_THROW("socket bind failure"); return sock; } std::vector get_messages(void* socket, int count = -1) { std::vector out; for ( ; count || count < 0; --count) { expect next = net::zmq::receive(socket, (count < 0 ? ZMQ_DONTWAIT : 0)); if (next == net::zmq::make_error_code(EAGAIN)) return out; out.push_back(std::move(*next)); } return out; } std::vector get_published(void* socket, int count = -1) { std::vector out; const auto messages = get_messages(socket, count); out.reserve(messages.size()); for (const std::string& message : messages) { const char* split = std::strchr(message.c_str(), ':'); if (!split) throw std::runtime_error{"Invalid ZMQ/Pub message"}; out.emplace_back(); out.back().first = {message.c_str(), split}; if (out.back().second.Parse(split + 1).HasParseError()) throw std::runtime_error{"Failed to parse ZMQ/Pub message"}; } return out; } testing::AssertionResult compare_full_txpool(epee::span events, const published_json& pub) { MASSERT(pub.first == "json-full-txpool_add"); MASSERT(pub.second.IsArray()); MASSERT(pub.second.Size() <= events.size()); std::size_t i = 0; for (const cryptonote::txpool_event& event : events) { MASSERT(i <= pub.second.Size()); if (!event.res) continue; cryptonote::transaction tx{}; cryptonote::json::fromJsonValue(pub.second[i], tx); crypto::hash id{}; MASSERT(cryptonote::get_transaction_hash(event.tx, id)); MASSERT(cryptonote::get_transaction_hash(tx, id)); MASSERT(event.tx.hash == tx.hash); ++i; } return testing::AssertionSuccess(); } testing::AssertionResult compare_minimal_txpool(epee::span events, const published_json& pub) { MASSERT(pub.first == "json-minimal-txpool_add"); MASSERT(pub.second.IsArray()); MASSERT(pub.second.Size() <= events.size()); std::size_t i = 0; for (const cryptonote::txpool_event& event : events) { MASSERT(i <= pub.second.Size()); if (!event.res) continue; std::size_t actual_size = 0; crypto::hash actual_id{}; MASSERT(pub.second[i].IsObject()); GET_FROM_JSON_OBJECT(pub.second[i], actual_id, id); GET_FROM_JSON_OBJECT(pub.second[i], actual_size, blob_size); std::size_t expected_size = 0; crypto::hash expected_id{}; MASSERT(cryptonote::get_transaction_hash(event.tx, expected_id, expected_size)); MASSERT(expected_size == actual_size); MASSERT(expected_id == actual_id); ++i; } return testing::AssertionSuccess(); } testing::AssertionResult compare_full_block(const epee::span expected, const published_json& pub) { MASSERT(pub.first == "json-full-chain_main"); MASSERT(pub.second.IsArray()); std::vector actual; cryptonote::json::fromJsonValue(pub.second, actual); MASSERT(expected.size() == actual.size()); for (std::size_t i = 0; i < expected.size(); ++i) { crypto::hash id; MASSERT(cryptonote::get_block_hash(expected[i], id)); MASSERT(cryptonote::get_block_hash(actual[i], id)); MASSERT(expected[i].hash == actual[i].hash); } return testing::AssertionSuccess(); } testing::AssertionResult compare_minimal_block(std::size_t height, const epee::span expected, const published_json& pub) { MASSERT(pub.first == "json-minimal-chain_main"); MASSERT(pub.second.IsObject()); MASSERT(!expected.empty()); std::size_t actual_height = 0; crypto::hash actual_prev_id{}; std::vector actual_ids{}; GET_FROM_JSON_OBJECT(pub.second, actual_height, first_height); GET_FROM_JSON_OBJECT(pub.second, actual_prev_id, first_prev_id); GET_FROM_JSON_OBJECT(pub.second, actual_ids, ids); MASSERT(height == actual_height); MASSERT(expected[0].prev_id == actual_prev_id); MASSERT(expected.size() == actual_ids.size()); for (std::size_t i = 0; i < expected.size(); ++i) { crypto::hash id; MASSERT(cryptonote::get_block_hash(expected[i], id)); MASSERT(id == actual_ids[i]); } return testing::AssertionSuccess(); } struct zmq_base : public testing::Test { cryptonote::account_base acct; zmq_base() : testing::Test(), acct() { acct.generate(); } cryptonote::transaction make_miner_transaction() { return test::make_miner_transaction(acct.get_keys().m_account_address); } cryptonote::transaction make_transaction(const std::vector& destinations) { return test::make_transaction(acct.get_keys(), {make_miner_transaction()}, destinations, true, true); } cryptonote::transaction make_transaction() { cryptonote::account_base temp_account; temp_account.generate(); return make_transaction({temp_account.get_keys().m_account_address}); } cryptonote::block make_block() { cryptonote::block block{}; block.major_version = 1; block.minor_version = 3; block.timestamp = 100; block.prev_id = crypto::rand(); block.nonce = 100; block.miner_tx = make_miner_transaction(); return block; } }; struct zmq_pub : public zmq_base { net::zmq::context ctx; net::zmq::socket relay; net::zmq::socket dummy_pub; net::zmq::socket dummy_client; std::shared_ptr pub; zmq_pub() : zmq_base(), ctx(zmq_init(1)), relay(create_socket(ctx.get(), cryptonote::listener::zmq_pub::relay_endpoint())), dummy_pub(create_socket(ctx.get(), inproc_pub)), dummy_client(zmq_socket(ctx.get(), ZMQ_PAIR)), pub(std::make_shared(ctx.get())) { if (!dummy_client) MONERO_ZMQ_THROW("failed to create socket"); if (zmq_connect(dummy_client.get(), inproc_pub) != 0) MONERO_ZMQ_THROW("failed to connect to dummy pub"); } virtual void TearDown() override final { EXPECT_EQ(0u, get_messages(relay.get()).size()); EXPECT_EQ(0u, get_messages(dummy_client.get()).size()); } template bool sub_request(const char (&topic)[N]) { return pub->sub_request({topic, N - 1}); } }; struct dummy_handler final : cryptonote::rpc::RpcHandler { dummy_handler() : cryptonote::rpc::RpcHandler() {} virtual epee::byte_slice handle(std::string&& request) override final { throw std::logic_error{"not implemented"}; } }; struct zmq_server : public zmq_base { dummy_handler handler; cryptonote::rpc::ZmqServer server; std::shared_ptr pub; net::zmq::socket sub; zmq_server() : zmq_base(), handler(), server(handler), pub(), sub() { void* ctx = server.init_rpc({}, {}); if (!ctx) throw std::runtime_error{"init_rpc failure"}; const std::string endpoint = inproc_pub; pub = server.init_pub({std::addressof(endpoint), 1}); if (!pub) throw std::runtime_error{"failed to initiaze zmq/pub"}; sub.reset(zmq_socket(ctx, ZMQ_SUB)); if (!sub) MONERO_ZMQ_THROW("failed to create socket"); if (zmq_connect(sub.get(), inproc_pub) != 0) MONERO_ZMQ_THROW("failed to connect to dummy pub"); server.run(); } virtual void TearDown() override final { EXPECT_EQ(0u, get_messages(sub.get()).size()); sub.reset(); pub.reset(); server.stop(); } template void subscribe(const char (&topic)[N]) { if (zmq_setsockopt(sub.get(), ZMQ_SUBSCRIBE, topic, N - 1) != 0) MONERO_ZMQ_THROW("failed to subscribe"); } }; } TEST_F(zmq_pub, InvalidContext) { EXPECT_THROW(cryptonote::listener::zmq_pub{nullptr}, std::logic_error); } TEST_F(zmq_pub, NoBlocking) { EXPECT_FALSE(pub->relay_to_pub(relay.get(), dummy_pub.get())); } TEST_F(zmq_pub, DefaultDrop) { EXPECT_EQ(0u, pub->send_txpool_add({{make_transaction(), {}, true}})); const cryptonote::block bl = make_block(); EXPECT_EQ(0u,pub->send_chain_main(5, {std::addressof(bl), 1})); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(5, {std::addressof(bl), 1})); } TEST_F(zmq_pub, JsonFullTxpool) { static constexpr const char topic[] = "\1json-full-txpool_add"; ASSERT_TRUE(sub_request(topic)); std::vector events { {make_transaction(), {}, true}, {make_transaction(), {}, true} }; EXPECT_NO_THROW(pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); events.at(0).res = false; EXPECT_EQ(1u, pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); events.at(0).res = false; EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); } TEST_F(zmq_pub, JsonMinimalTxpool) { static constexpr const char topic[] = "\1json-minimal-txpool_add"; ASSERT_TRUE(sub_request(topic)); std::vector events { {make_transaction(), {}, true}, {make_transaction(), {}, true} }; EXPECT_NO_THROW(pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); events.at(0).res = false; EXPECT_EQ(1u, pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); events.at(0).res = false; EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); } TEST_F(zmq_pub, JsonFullChain) { static constexpr const char topic[] = "\1json-full-chain_main"; ASSERT_TRUE(sub_request(topic)); const std::array blocks{{make_block(), make_block()}}; EXPECT_EQ(1u, pub->send_chain_main(100, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); } TEST_F(zmq_pub, JsonMinimalChain) { static constexpr const char topic[] = "\1json-minimal-chain_main"; ASSERT_TRUE(sub_request(topic)); const std::array blocks{{make_block(), make_block()}}; EXPECT_EQ(1u, pub->send_chain_main(100, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_block(100, epee::to_span(blocks), pubs.front())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_block(533, epee::to_span(blocks), pubs.front())); } TEST_F(zmq_pub, JsonFullAll) { static constexpr const char topic[] = "\1json-full"; ASSERT_TRUE(sub_request(topic)); { std::vector events { {make_transaction(), {}, true}, {make_transaction(), {}, true} }; EXPECT_EQ(1u, pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); events.at(0).res = false; EXPECT_NO_THROW(pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); events.at(0).res = false; EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); } { const std::array blocks{{make_block(), make_block()}}; EXPECT_EQ(1u, pub->send_chain_main(100, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); } } TEST_F(zmq_pub, JsonMinimalAll) { static constexpr const char topic[] = "\1json-minimal"; ASSERT_TRUE(sub_request(topic)); { std::vector events { {make_transaction(), {}, true}, {make_transaction(), {}, true} }; EXPECT_EQ(1u, pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); events.at(0).res = false; EXPECT_NO_THROW(pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); events.at(0).res = false; EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); } { const std::array blocks{{make_block(), make_block()}}; EXPECT_EQ(1u, pub->send_chain_main(100, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_block(100, epee::to_span(blocks), pubs.front())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(1u, pubs.size()); ASSERT_LE(1u, pubs.size()); EXPECT_TRUE(compare_minimal_block(533, epee::to_span(blocks), pubs.front())); } } TEST_F(zmq_pub, JsonAll) { static constexpr const char topic[] = "\1json"; ASSERT_TRUE(sub_request(topic)); { std::vector events { {make_transaction(), {}, true}, {make_transaction(), {}, true} }; EXPECT_EQ(1u, pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(2u, pubs.size()); ASSERT_LE(2u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.back())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(2u, pubs.size()); ASSERT_LE(2u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.back())); events.at(0).res = false; EXPECT_EQ(1u, pub->send_txpool_add(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(2u, pubs.size()); ASSERT_LE(2u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.back())); events.at(0).res = false; EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(events)); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(2u, pubs.size()); ASSERT_LE(2u, pubs.size()); EXPECT_TRUE(compare_full_txpool(epee::to_span(events), pubs.front())); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.back())); } { const std::array blocks{{make_block()}}; EXPECT_EQ(2u, pub->send_chain_main(100, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); auto pubs = get_published(dummy_client.get()); EXPECT_EQ(2u, pubs.size()); ASSERT_LE(2u, pubs.size()); EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); EXPECT_TRUE(compare_minimal_block(100, epee::to_span(blocks), pubs.back())); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); EXPECT_TRUE(pub->relay_to_pub(relay.get(), dummy_pub.get())); pubs = get_published(dummy_client.get()); EXPECT_EQ(2u, pubs.size()); ASSERT_LE(2u, pubs.size()); EXPECT_TRUE(compare_full_block(epee::to_span(blocks), pubs.front())); EXPECT_TRUE(compare_minimal_block(533, epee::to_span(blocks), pubs.back())); } } TEST_F(zmq_pub, JsonChainWeakPtrSkip) { static constexpr const char topic[] = "\1json"; ASSERT_TRUE(sub_request(topic)); const std::array blocks{{make_block()}}; pub.reset(); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::chain_main{pub}(533, epee::to_span(blocks))); } TEST_F(zmq_pub, JsonTxpoolWeakPtrSkip) { static constexpr const char topic[] = "\1json"; ASSERT_TRUE(sub_request(topic)); std::vector events { {make_transaction(), {}, true}, {make_transaction(), {}, true} }; pub.reset(); EXPECT_NO_THROW(cryptonote::listener::zmq_pub::txpool_add{pub}(std::move(events))); } TEST_F(zmq_server, pub) { subscribe("json-minimal"); std::vector events { {make_transaction(), {}, true}, {make_transaction(), {}, true} }; const std::array blocks{{make_block()}}; ASSERT_EQ(1u, pub->send_txpool_add(events)); ASSERT_EQ(1u, pub->send_chain_main(200, epee::to_span(blocks))); auto pubs = get_published(sub.get(), 2); EXPECT_EQ(2u, pubs.size()); ASSERT_LE(2u, pubs.size()); EXPECT_TRUE(compare_minimal_txpool(epee::to_span(events), pubs.front())); EXPECT_TRUE(compare_minimal_block(200, epee::to_span(blocks), pubs.back())); }