aboutsummaryrefslogtreecommitdiff
path: root/tests/unit_tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit_tests')
-rw-r--r--tests/unit_tests/CMakeLists.txt2
-rw-r--r--tests/unit_tests/account.cpp34
-rw-r--r--tests/unit_tests/json_serialization.cpp36
-rw-r--r--tests/unit_tests/json_serialization.h42
-rw-r--r--tests/unit_tests/levin.cpp254
-rw-r--r--tests/unit_tests/serialization.cpp40
-rw-r--r--tests/unit_tests/tx_proof.cpp130
-rw-r--r--tests/unit_tests/zmq_rpc.cpp722
8 files changed, 1243 insertions, 17 deletions
diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt
index ef0477888..a5984b2c9 100644
--- a/tests/unit_tests/CMakeLists.txt
+++ b/tests/unit_tests/CMakeLists.txt
@@ -83,6 +83,7 @@ set(unit_tests_sources
test_peerlist.cpp
test_protocol_pack.cpp
threadpool.cpp
+ tx_proof.cpp
hardfork.cpp
unbound.cpp
uri.cpp
@@ -109,6 +110,7 @@ target_link_libraries(unit_tests
cryptonote_protocol
cryptonote_core
daemon_messages
+ daemon_rpc_server
blockchain_db
lmdb_lib
rpc
diff --git a/tests/unit_tests/account.cpp b/tests/unit_tests/account.cpp
index 2ab2f893a..68bf4dce7 100644
--- a/tests/unit_tests/account.cpp
+++ b/tests/unit_tests/account.cpp
@@ -29,14 +29,30 @@
#include "gtest/gtest.h"
#include "cryptonote_basic/account.h"
+#include "ringct/rctOps.h"
+// Tests in-memory encryption of account secret keys
TEST(account, encrypt_keys)
{
+ // Generate account keys and random multisig keys
cryptonote::keypair recovery_key = cryptonote::keypair::generate(hw::get_device("default"));
cryptonote::account_base account;
crypto::secret_key key = account.generate(recovery_key.sec);
+
+ const size_t n_multisig = 4;
+ std::vector<crypto::secret_key> multisig_keys;
+ multisig_keys.reserve(n_multisig);
+ multisig_keys.resize(0);
+ for (size_t i = 0; i < n_multisig; ++i)
+ {
+ multisig_keys.push_back(rct::rct2sk(rct::skGen()));
+ }
+ ASSERT_TRUE(account.make_multisig(account.get_keys().m_view_secret_key, account.get_keys().m_spend_secret_key, account.get_keys().m_account_address.m_spend_public_key, multisig_keys));
+
const cryptonote::account_keys keys = account.get_keys();
+ ASSERT_EQ(keys.m_multisig_keys.size(),n_multisig);
+ // Encrypt and decrypt keys
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
@@ -50,22 +66,40 @@ TEST(account, encrypt_keys)
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+ ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys);
account.decrypt_viewkey(chacha_key);
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+ ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys);
account.encrypt_viewkey(chacha_key);
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+ ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys);
+
+ account.decrypt_viewkey(chacha_key);
+
+ ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
+ ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
+ ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+ ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys);
+
+ account.encrypt_viewkey(chacha_key);
+
+ ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
+ ASSERT_NE(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
+ ASSERT_NE(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+ ASSERT_NE(account.get_keys().m_multisig_keys, keys.m_multisig_keys);
account.decrypt_keys(chacha_key);
ASSERT_EQ(account.get_keys().m_account_address, keys.m_account_address);
ASSERT_EQ(account.get_keys().m_spend_secret_key, keys.m_spend_secret_key);
ASSERT_EQ(account.get_keys().m_view_secret_key, keys.m_view_secret_key);
+ ASSERT_EQ(account.get_keys().m_multisig_keys, keys.m_multisig_keys);
}
diff --git a/tests/unit_tests/json_serialization.cpp b/tests/unit_tests/json_serialization.cpp
index 5873d0ab6..f76199e57 100644
--- a/tests/unit_tests/json_serialization.cpp
+++ b/tests/unit_tests/json_serialization.cpp
@@ -15,7 +15,7 @@
#include "serialization/json_object.h"
-namespace
+namespace test
{
cryptonote::transaction
make_miner_transaction(cryptonote::account_public_address const& to)
@@ -82,7 +82,10 @@ namespace
return tx;
}
+}
+namespace
+{
template<typename T>
T test_json(const T& value)
{
@@ -94,7 +97,7 @@ namespace
rapidjson::Document doc;
doc.Parse(reinterpret_cast<const char*>(buffer.data()), buffer.size());
- if (doc.HasParseError() || !doc.IsObject())
+ if (doc.HasParseError())
{
throw cryptonote::json::PARSE_FAIL();
}
@@ -105,11 +108,26 @@ namespace
}
} // anonymous
+TEST(JsonSerialization, VectorBytes)
+{
+ EXPECT_EQ(std::vector<std::uint8_t>{}, test_json(std::vector<std::uint8_t>{}));
+ EXPECT_EQ(std::vector<std::uint8_t>{0x00}, test_json(std::vector<std::uint8_t>{0x00}));
+}
+
+TEST(JsonSerialization, InvalidVectorBytes)
+{
+ rapidjson::Document doc;
+ doc.SetString("1");
+
+ std::vector<std::uint8_t> out;
+ EXPECT_THROW(cryptonote::json::fromJsonValue(doc, out), cryptonote::json::BAD_INPUT);
+}
+
TEST(JsonSerialization, MinerTransaction)
{
cryptonote::account_base acct;
acct.generate();
- const auto miner_tx = make_miner_transaction(acct.get_keys().m_account_address);
+ const auto miner_tx = test::make_miner_transaction(acct.get_keys().m_account_address);
crypto::hash tx_hash{};
ASSERT_TRUE(cryptonote::get_transaction_hash(miner_tx, tx_hash));
@@ -137,8 +155,8 @@ TEST(JsonSerialization, RegularTransaction)
cryptonote::account_base acct2;
acct2.generate();
- const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address);
- const auto tx = make_transaction(
+ const auto miner_tx = test::make_miner_transaction(acct1.get_keys().m_account_address);
+ const auto tx = test::make_transaction(
acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, false, false
);
@@ -168,8 +186,8 @@ TEST(JsonSerialization, RingctTransaction)
cryptonote::account_base acct2;
acct2.generate();
- const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address);
- const auto tx = make_transaction(
+ const auto miner_tx = test::make_miner_transaction(acct1.get_keys().m_account_address);
+ const auto tx = test::make_transaction(
acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, false
);
@@ -199,8 +217,8 @@ TEST(JsonSerialization, BulletproofTransaction)
cryptonote::account_base acct2;
acct2.generate();
- const auto miner_tx = make_miner_transaction(acct1.get_keys().m_account_address);
- const auto tx = make_transaction(
+ const auto miner_tx = test::make_miner_transaction(acct1.get_keys().m_account_address);
+ const auto tx = test::make_transaction(
acct1.get_keys(), {miner_tx}, {acct2.get_keys().m_account_address}, true, true
);
diff --git a/tests/unit_tests/json_serialization.h b/tests/unit_tests/json_serialization.h
new file mode 100644
index 000000000..2d8267261
--- /dev/null
+++ b/tests/unit_tests/json_serialization.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2020, 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.
+
+#pragma once
+
+namespace test
+{
+ cryptonote::transaction make_miner_transaction(cryptonote::account_public_address const& to);
+
+ cryptonote::transaction
+ make_transaction(
+ cryptonote::account_keys const& from,
+ std::vector<cryptonote::transaction> const& sources,
+ std::vector<cryptonote::account_public_address> const& destinations,
+ bool rct,
+ bool bulletproof);
+}
diff --git a/tests/unit_tests/levin.cpp b/tests/unit_tests/levin.cpp
index d9d273837..15563e764 100644
--- a/tests/unit_tests/levin.cpp
+++ b/tests/unit_tests/levin.cpp
@@ -680,6 +680,76 @@ TEST_F(levin_notify, local_without_padding)
}
}
+TEST_F(levin_notify, forward_without_padding)
+{
+ cryptonote::levin::notify notifier = make_notifier(0, true, false);
+
+ for (unsigned count = 0; count < 10; ++count)
+ add_connection(count % 2 == 0);
+
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled);
+ }
+ notifier.new_out_connection();
+ io_service_.poll();
+
+ std::vector<cryptonote::blobdata> txs(2);
+ txs[0].resize(100, 'f');
+ txs[1].resize(200, 'e');
+
+ std::vector<cryptonote::blobdata> sorted_txs = txs;
+ std::sort(sorted_txs.begin(), sorted_txs.end());
+
+ ASSERT_EQ(10u, contexts_.size());
+ bool has_stemmed = false;
+ bool has_fluffed = false;
+ while (!has_stemmed || !has_fluffed)
+ {
+ auto context = contexts_.begin();
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward));
+
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+ const bool is_stem = events_.has_stem_txes();
+ EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff));
+
+ if (!is_stem)
+ {
+ notifier.run_fluff();
+ ASSERT_LT(0u, io_service_.poll());
+ }
+
+ std::size_t send_count = 0;
+ EXPECT_EQ(0u, context->process_send_queue());
+ for (++context; context != contexts_.end(); ++context)
+ {
+ const std::size_t sent = context->process_send_queue();
+ if (sent && is_stem)
+ EXPECT_EQ(1u, (context - contexts_.begin()) % 2);
+ send_count += sent;
+ }
+
+ EXPECT_EQ(is_stem ? 1u : 9u, send_count);
+ ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size());
+ for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count)
+ {
+ auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
+ if (is_stem)
+ EXPECT_EQ(txs, notification.txs);
+ else
+ EXPECT_EQ(sorted_txs, notification.txs);
+ EXPECT_TRUE(notification._.empty());
+ EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
+ }
+
+ has_stemmed |= is_stem;
+ has_fluffed |= !is_stem;
+ notifier.run_epoch();
+ }
+}
+
TEST_F(levin_notify, block_without_padding)
{
cryptonote::levin::notify notifier = make_notifier(0, true, false);
@@ -918,6 +988,73 @@ TEST_F(levin_notify, local_with_padding)
}
}
+TEST_F(levin_notify, forward_with_padding)
+{
+ cryptonote::levin::notify notifier = make_notifier(0, true, true);
+
+ for (unsigned count = 0; count < 10; ++count)
+ add_connection(count % 2 == 0);
+
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled);
+ }
+ notifier.new_out_connection();
+ io_service_.poll();
+
+ std::vector<cryptonote::blobdata> txs(2);
+ txs[0].resize(100, 'e');
+ txs[1].resize(200, 'f');
+
+ ASSERT_EQ(10u, contexts_.size());
+ bool has_stemmed = false;
+ bool has_fluffed = false;
+ while (!has_stemmed || !has_fluffed)
+ {
+ auto context = contexts_.begin();
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward));
+
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+ const bool is_stem = events_.has_stem_txes();
+ EXPECT_EQ(txs, events_.take_relayed(is_stem ? cryptonote::relay_method::stem : cryptonote::relay_method::fluff));
+
+ if (!is_stem)
+ {
+ notifier.run_fluff();
+ ASSERT_LT(0u, io_service_.poll());
+ }
+
+ std::size_t send_count = 0;
+ EXPECT_EQ(0u, context->process_send_queue());
+ for (++context; context != contexts_.end(); ++context)
+ {
+ const std::size_t sent = context->process_send_queue();
+ if (sent && is_stem)
+ {
+ EXPECT_EQ(1u, (context - contexts_.begin()) % 2);
+ EXPECT_FALSE(context->is_incoming());
+ }
+ send_count += sent;
+ }
+
+ EXPECT_EQ(is_stem ? 1u : 9u, send_count);
+ ASSERT_EQ(is_stem ? 1u : 9u, receiver_.notified_size());
+ for (unsigned count = 0; count < (is_stem ? 1u : 9u); ++count)
+ {
+ auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
+ EXPECT_EQ(txs, notification.txs);
+ EXPECT_FALSE(notification._.empty());
+ EXPECT_EQ(!is_stem, notification.dandelionpp_fluff);
+ }
+
+ has_stemmed |= is_stem;
+ has_fluffed |= !is_stem;
+ notifier.run_epoch();
+ }
+}
+
TEST_F(levin_notify, block_with_padding)
{
cryptonote::levin::notify notifier = make_notifier(0, true, true);
@@ -1021,7 +1158,7 @@ TEST_F(levin_notify, private_fluff_without_padding)
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs);
EXPECT_TRUE(notification._.empty());
- EXPECT_FALSE(notification.dandelionpp_fluff);
+ EXPECT_TRUE(notification.dandelionpp_fluff);
}
}
}
@@ -1057,7 +1194,7 @@ TEST_F(levin_notify, private_stem_without_padding)
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
- EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff));
+ EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem));
EXPECT_EQ(0u, context->process_send_queue());
for (++context; context != contexts_.end(); ++context)
@@ -1072,7 +1209,7 @@ TEST_F(levin_notify, private_stem_without_padding)
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs);
EXPECT_TRUE(notification._.empty());
- EXPECT_FALSE(notification.dandelionpp_fluff);
+ EXPECT_TRUE(notification.dandelionpp_fluff);
}
}
}
@@ -1123,7 +1260,58 @@ TEST_F(levin_notify, private_local_without_padding)
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs);
EXPECT_TRUE(notification._.empty());
- EXPECT_FALSE(notification.dandelionpp_fluff);
+ EXPECT_TRUE(notification.dandelionpp_fluff);
+ }
+ }
+}
+
+TEST_F(levin_notify, private_forward_without_padding)
+{
+ // private mode always uses fluff but marked as stem
+ cryptonote::levin::notify notifier = make_notifier(0, false, false);
+
+ for (unsigned count = 0; count < 10; ++count)
+ add_connection(count % 2 == 0);
+
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled);
+ }
+ notifier.new_out_connection();
+ io_service_.poll();
+
+ std::vector<cryptonote::blobdata> txs(2);
+ txs[0].resize(100, 'e');
+ txs[1].resize(200, 'f');
+
+ ASSERT_EQ(10u, contexts_.size());
+ {
+ auto context = contexts_.begin();
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward));
+
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+ notifier.run_fluff();
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+
+ EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::forward));
+
+ EXPECT_EQ(0u, context->process_send_queue());
+ for (++context; context != contexts_.end(); ++context)
+ {
+ const bool is_incoming = ((context - contexts_.begin()) % 2 == 0);
+ EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue());
+ }
+
+ ASSERT_EQ(5u, receiver_.notified_size());
+ for (unsigned count = 0; count < 5; ++count)
+ {
+ auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
+ EXPECT_EQ(txs, notification.txs);
+ EXPECT_TRUE(notification._.empty());
+ EXPECT_TRUE(notification.dandelionpp_fluff);
}
}
}
@@ -1233,7 +1421,7 @@ TEST_F(levin_notify, private_fluff_with_padding)
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty());
- EXPECT_FALSE(notification.dandelionpp_fluff);
+ EXPECT_TRUE(notification.dandelionpp_fluff);
}
}
}
@@ -1268,7 +1456,7 @@ TEST_F(levin_notify, private_stem_with_padding)
io_service_.reset();
ASSERT_LT(0u, io_service_.poll());
- EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::fluff));
+ EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::stem));
EXPECT_EQ(0u, context->process_send_queue());
for (++context; context != contexts_.end(); ++context)
@@ -1283,7 +1471,7 @@ TEST_F(levin_notify, private_stem_with_padding)
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty());
- EXPECT_FALSE(notification.dandelionpp_fluff);
+ EXPECT_TRUE(notification.dandelionpp_fluff);
}
}
}
@@ -1333,7 +1521,57 @@ TEST_F(levin_notify, private_local_with_padding)
auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
EXPECT_EQ(txs, notification.txs);
EXPECT_FALSE(notification._.empty());
- EXPECT_FALSE(notification.dandelionpp_fluff);
+ EXPECT_TRUE(notification.dandelionpp_fluff);
+ }
+ }
+}
+
+TEST_F(levin_notify, private_forward_with_padding)
+{
+ cryptonote::levin::notify notifier = make_notifier(0, false, true);
+
+ for (unsigned count = 0; count < 10; ++count)
+ add_connection(count % 2 == 0);
+
+ {
+ const auto status = notifier.get_status();
+ EXPECT_FALSE(status.has_noise);
+ EXPECT_FALSE(status.connections_filled);
+ }
+ notifier.new_out_connection();
+ io_service_.poll();
+
+ std::vector<cryptonote::blobdata> txs(2);
+ txs[0].resize(100, 'e');
+ txs[1].resize(200, 'f');
+
+ ASSERT_EQ(10u, contexts_.size());
+ {
+ auto context = contexts_.begin();
+ EXPECT_TRUE(notifier.send_txs(txs, context->get_id(), events_, cryptonote::relay_method::forward));
+
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+ notifier.run_fluff();
+ io_service_.reset();
+ ASSERT_LT(0u, io_service_.poll());
+
+ EXPECT_EQ(txs, events_.take_relayed(cryptonote::relay_method::forward));
+
+ EXPECT_EQ(0u, context->process_send_queue());
+ for (++context; context != contexts_.end(); ++context)
+ {
+ const bool is_incoming = ((context - contexts_.begin()) % 2 == 0);
+ EXPECT_EQ(is_incoming ? 0u : 1u, context->process_send_queue());
+ }
+
+ ASSERT_EQ(5u, receiver_.notified_size());
+ for (unsigned count = 0; count < 5; ++count)
+ {
+ auto notification = receiver_.get_notification<cryptonote::NOTIFY_NEW_TRANSACTIONS>().second;
+ EXPECT_EQ(txs, notification.txs);
+ EXPECT_FALSE(notification._.empty());
+ EXPECT_TRUE(notification.dandelionpp_fluff);
}
}
}
diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp
index ee205e666..b460559ff 100644
--- a/tests/unit_tests/serialization.cpp
+++ b/tests/unit_tests/serialization.cpp
@@ -616,6 +616,46 @@ TEST(Serialization, serializes_ringct_types)
ASSERT_EQ(bp0, bp1);
}
+TEST(Serialization, key_encryption_transition)
+{
+ const cryptonote::network_type nettype = cryptonote::TESTNET;
+ tools::wallet2 w(nettype);
+ const boost::filesystem::path wallet_file = unit_test::data_dir / "wallet_9svHk1";
+ const boost::filesystem::path key_file = unit_test::data_dir / "wallet_9svHk1.keys";
+ const boost::filesystem::path temp_wallet_file = unit_test::data_dir / "wallet_9svHk1_temp";
+ const boost::filesystem::path temp_key_file = unit_test::data_dir / "wallet_9svHk1_temp.keys";
+ string password = "test";
+ bool r = false;
+
+ // Copy the original files for this test
+ boost::filesystem::copy(wallet_file,temp_wallet_file);
+ boost::filesystem::copy(key_file,temp_key_file);
+
+ try
+ {
+ // Key transition
+ w.load(temp_wallet_file.string(), password); // legacy decryption method
+ ASSERT_TRUE(w.get_load_info().is_legacy_key_encryption);
+ const crypto::secret_key view_secret_key = w.get_account().get_keys().m_view_secret_key;
+
+ w.rewrite(temp_wallet_file.string(), password); // transition to new key format
+
+ w.load(temp_wallet_file.string(), password); // new decryption method
+ ASSERT_FALSE(w.get_load_info().is_legacy_key_encryption);
+ ASSERT_EQ(w.get_account().get_keys().m_view_secret_key,view_secret_key);
+
+ r = true;
+ }
+ catch (const exception& e)
+ {}
+
+ // Remove the temporary files
+ boost::filesystem::remove(temp_wallet_file);
+ boost::filesystem::remove(temp_key_file);
+
+ ASSERT_TRUE(r);
+}
+
TEST(Serialization, portability_wallet)
{
const cryptonote::network_type nettype = cryptonote::TESTNET;
diff --git a/tests/unit_tests/tx_proof.cpp b/tests/unit_tests/tx_proof.cpp
new file mode 100644
index 000000000..c5d06bc68
--- /dev/null
+++ b/tests/unit_tests/tx_proof.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2018, 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 "gtest/gtest.h"
+
+#include "crypto/crypto.h"
+extern "C" {
+#include "crypto/crypto-ops.h"
+}
+#include "crypto/hash.h"
+#include <boost/algorithm/string.hpp>
+
+static inline unsigned char *operator &(crypto::ec_point &point) {
+ return &reinterpret_cast<unsigned char &>(point);
+ }
+
+static inline unsigned char *operator &(crypto::ec_scalar &scalar) {
+ return &reinterpret_cast<unsigned char &>(scalar);
+ }
+
+TEST(tx_proof, prove_verify_v2)
+{
+ crypto::secret_key r;
+ crypto::random32_unbiased(&r);
+
+ // A = aG
+ // B = bG
+ crypto::secret_key a,b;
+ crypto::public_key A,B;
+ crypto::generate_keys(A, a, a, false);
+ crypto::generate_keys(B, b, b, false);
+
+ // R_B = rB
+ crypto::public_key R_B;
+ ge_p3 B_p3;
+ ge_frombytes_vartime(&B_p3,&B);
+ ge_p2 R_B_p2;
+ ge_scalarmult(&R_B_p2, &unwrap(r), &B_p3);
+ ge_tobytes(&R_B, &R_B_p2);
+
+ // R_G = rG
+ crypto::public_key R_G;
+ ge_frombytes_vartime(&B_p3,&B);
+ ge_p3 R_G_p3;
+ ge_scalarmult_base(&R_G_p3, &unwrap(r));
+ ge_p3_tobytes(&R_G, &R_G_p3);
+
+ // D = rA
+ crypto::public_key D;
+ ge_p3 A_p3;
+ ge_frombytes_vartime(&A_p3,&A);
+ ge_p2 D_p2;
+ ge_scalarmult(&D_p2, &unwrap(r), &A_p3);
+ ge_tobytes(&D, &D_p2);
+
+ crypto::signature sig;
+
+ // Message data
+ crypto::hash prefix_hash;
+ char data[] = "hash input";
+ crypto::cn_fast_hash(data,sizeof(data)-1,prefix_hash);
+
+ // Generate/verify valid v1 proof with standard address
+ crypto::generate_tx_proof_v1(prefix_hash, R_G, A, boost::none, D, r, sig);
+ ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 1));
+
+ // Generate/verify valid v1 proof with subaddress
+ crypto::generate_tx_proof_v1(prefix_hash, R_B, A, B, D, r, sig);
+ ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 1));
+
+ // Generate/verify valid v2 proof with standard address
+ crypto::generate_tx_proof(prefix_hash, R_G, A, boost::none, D, r, sig);
+ ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 2));
+
+ // Generate/verify valid v2 proof with subaddress
+ crypto::generate_tx_proof(prefix_hash, R_B, A, B, D, r, sig);
+ ASSERT_TRUE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 2));
+
+ // Try to verify valid v2 proofs as v1 proof (bad)
+ crypto::generate_tx_proof(prefix_hash, R_G, A, boost::none, D, r, sig);
+ ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 1));
+ crypto::generate_tx_proof(prefix_hash, R_B, A, B, D, r, sig);
+ ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 1));
+
+ // Randomly-distributed test points
+ crypto::secret_key evil_a, evil_b, evil_d, evil_r;
+ crypto::public_key evil_A, evil_B, evil_D, evil_R;
+ crypto::generate_keys(evil_A, evil_a, evil_a, false);
+ crypto::generate_keys(evil_B, evil_b, evil_b, false);
+ crypto::generate_keys(evil_D, evil_d, evil_d, false);
+ crypto::generate_keys(evil_R, evil_r, evil_r, false);
+
+ // Selectively choose bad point in v2 proof (bad)
+ crypto::generate_tx_proof(prefix_hash, R_B, A, B, D, r, sig);
+ ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, evil_R, A, B, D, sig, 2));
+ ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, evil_A, B, D, sig, 2));
+ ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, evil_B, D, sig, 2));
+ ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, B, evil_D, sig, 2));
+
+ // Try to verify valid v1 proofs as v2 proof (bad)
+ crypto::generate_tx_proof_v1(prefix_hash, R_G, A, boost::none, D, r, sig);
+ ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_G, A, boost::none, D, sig, 2));
+ crypto::generate_tx_proof_v1(prefix_hash, R_B, A, B, D, r, sig);
+ ASSERT_FALSE(crypto::check_tx_proof(prefix_hash, R_B, A, B, D, sig, 2));
+}
diff --git a/tests/unit_tests/zmq_rpc.cpp b/tests/unit_tests/zmq_rpc.cpp
index af1f1608b..59759bed8 100644
--- a/tests/unit_tests/zmq_rpc.cpp
+++ b/tests/unit_tests/zmq_rpc.cpp
@@ -26,11 +26,25 @@
// 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 <boost/preprocessor/stringize.hpp>
#include <gtest/gtest.h>
+#include <rapidjson/document.h>
+#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(
@@ -53,3 +67,711 @@ TEST(ZmqFullMessage, Request)
cryptonote::rpc::FullMessage parsed{request, true};
EXPECT_STREQ("foo", parsed.getRequestType().c_str());
}
+
+namespace
+{
+ using published_json = std::pair<std::string, rapidjson::Document>;
+
+ 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<std::string> get_messages(void* socket, int count = -1)
+ {
+ std::vector<std::string> out;
+ for ( ; count || count < 0; --count)
+ {
+ expect<std::string> 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<published_json> get_published(void* socket, int count = -1)
+ {
+ std::vector<published_json> 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<const cryptonote::txpool_event> 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<const cryptonote::txpool_event> 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<const cryptonote::block> expected, const published_json& pub)
+ {
+ MASSERT(pub.first == "json-full-chain_main");
+ MASSERT(pub.second.IsArray());
+
+ std::vector<cryptonote::block> 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<const cryptonote::block> 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_id{};
+ crypto::hash actual_prev_id{};
+ std::vector<crypto::hash> 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<cryptonote::account_public_address>& 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<crypto::hash>();
+ 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<cryptonote::listener::zmq_pub> 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<cryptonote::listener::zmq_pub>(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<std::size_t N>
+ 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<cryptonote::listener::zmq_pub> 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<std::size_t N>
+ 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<cryptonote::txpool_event> 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<cryptonote::txpool_event> 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<cryptonote::block, 2> 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<cryptonote::block, 2> 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<cryptonote::txpool_event> 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<cryptonote::block, 2> 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<cryptonote::txpool_event> 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<cryptonote::block, 2> 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<cryptonote::txpool_event> 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<cryptonote::block, 1> 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<cryptonote::block, 1> 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<cryptonote::txpool_event> 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<cryptonote::txpool_event> events
+ {
+ {make_transaction(), {}, true}, {make_transaction(), {}, true}
+ };
+
+ const std::array<cryptonote::block, 1> 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()));
+}