From ae3c3e396aad17bd6a752cc2e2b4d4b310e0fbdf Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Thu, 20 Feb 2020 12:02:07 +0000 Subject: [PATCH] Add tests for vote_processor (#2574) --- nano/core_test/CMakeLists.txt | 1 + nano/core_test/vote_processor.cpp | 142 ++++++++++++++++++++++++++++++ nano/node/vote_processor.cpp | 12 +++ nano/node/vote_processor.hpp | 3 + 4 files changed, 158 insertions(+) create mode 100644 nano/core_test/vote_processor.cpp diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index 314c5effa5..947d5dff0d 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable (core_test uint256_union.cpp utility.cpp versioning.cpp + vote_processor.cpp wallet.cpp wallets.cpp websocket.cpp diff --git a/nano/core_test/vote_processor.cpp b/nano/core_test/vote_processor.cpp new file mode 100644 index 0000000000..5ff9b92440 --- /dev/null +++ b/nano/core_test/vote_processor.cpp @@ -0,0 +1,142 @@ +#include +#include +#include +#include + +#include + +using namespace std::chrono_literals; + +TEST (vote_processor, codes) +{ + nano::system system (1); + auto & node (*system.nodes[0]); + nano::genesis genesis; + nano::keypair key; + auto vote (std::make_shared (key.pub, key.prv, 1, std::vector{ genesis.open->hash () })); + auto vote_invalid = std::make_shared (*vote); + vote_invalid->signature.bytes[0] ^= 1; + auto channel (std::make_shared (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version)); + + // Invalid signature + ASSERT_EQ (nano::vote_code::invalid, node.vote_processor.vote_blocking (vote_invalid, channel, false)); + + // Hint of pre-validation + ASSERT_NE (nano::vote_code::invalid, node.vote_processor.vote_blocking (vote_invalid, channel, true)); + + // No ongoing election + ASSERT_EQ (nano::vote_code::indeterminate, node.vote_processor.vote_blocking (vote, channel)); + + // First vote from an account for an ongoing election + ASSERT_TRUE (node.active.insert (genesis.open).second); + ASSERT_EQ (nano::vote_code::vote, node.vote_processor.vote_blocking (vote, channel)); + + // Processing the same vote is a replay + ASSERT_EQ (nano::vote_code::replay, node.vote_processor.vote_blocking (vote, channel)); + + // Invalid takes precedence + ASSERT_EQ (nano::vote_code::invalid, node.vote_processor.vote_blocking (vote_invalid, channel)); + + // A higher sequence is not a replay + ++vote->sequence; + ASSERT_EQ (nano::vote_code::invalid, node.vote_processor.vote_blocking (vote, channel)); + vote->signature = nano::sign_message (key.prv, key.pub, vote->hash ()); + ASSERT_EQ (nano::vote_code::vote, node.vote_processor.vote_blocking (vote, channel)); + + // Once the election is removed (confirmed / dropped) the vote is again indeterminate + node.active.erase (*genesis.open); + ASSERT_EQ (nano::vote_code::indeterminate, node.vote_processor.vote_blocking (vote, channel)); +} + +TEST (vote_processor, flush) +{ + nano::system system (1); + auto & node (*system.nodes[0]); + nano::genesis genesis; + auto vote (std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, std::vector{ genesis.open->hash () })); + auto channel (std::make_shared (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version)); + for (unsigned i = 0; i < 2000; ++i) + { + node.vote_processor.vote (vote, channel); + ++vote->sequence; // invalidates votes without signing again + } + node.vote_processor.flush (); + ASSERT_TRUE (node.vote_processor.empty ()); +} + +TEST (vote_processor, invalid_signature) +{ + nano::system system (1); + auto & node (*system.nodes[0]); + nano::genesis genesis; + nano::keypair key; + auto vote (std::make_shared (key.pub, key.prv, 1, std::vector{ genesis.open->hash () })); + auto vote_invalid = std::make_shared (*vote); + vote_invalid->signature.bytes[0] ^= 1; + auto channel (std::make_shared (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version)); + + auto election (node.active.insert (genesis.open)); + ASSERT_TRUE (election.first && election.second); + ASSERT_EQ (1, election.first->last_votes.size ()); + node.vote_processor.vote (vote_invalid, channel); + node.vote_processor.flush (); + ASSERT_EQ (1, election.first->last_votes.size ()); + node.vote_processor.vote (vote, channel); + node.vote_processor.flush (); + ASSERT_EQ (2, election.first->last_votes.size ()); +} + +namespace nano +{ +TEST (vote_processor, weights) +{ + nano::system system (4); + auto & node (*system.nodes[0]); + + // Create representatives of different weight levels + // The online stake will be the minimum configurable due to online_reps sampling in tests + auto const online = node.config.online_weight_minimum.number (); + auto const level0 = online / 5000; // 0.02% + auto const level1 = online / 500; // 0.2% + auto const level2 = online / 50; // 2% + + nano::keypair key0; + nano::keypair key1; + nano::keypair key2; + + system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); + system.wallet (1)->insert_adhoc (key0.prv); + system.wallet (2)->insert_adhoc (key1.prv); + system.wallet (3)->insert_adhoc (key2.prv); + system.wallet (1)->store.representative_set (system.nodes[1]->wallets.tx_begin_write (), key0.pub); + system.wallet (2)->store.representative_set (system.nodes[2]->wallets.tx_begin_write (), key1.pub); + system.wallet (3)->store.representative_set (system.nodes[3]->wallets.tx_begin_write (), key2.pub); + system.wallet (0)->send_sync (nano::test_genesis_key.pub, key0.pub, level0); + system.wallet (0)->send_sync (nano::test_genesis_key.pub, key1.pub, level1); + system.wallet (0)->send_sync (nano::test_genesis_key.pub, key2.pub, level2); + + // Wait for representatives + system.deadline_set (10s); + while (node.ledger.cache.rep_weights.get_rep_amounts ().size () != 4) + { + ASSERT_NO_ERROR (system.poll ()); + } + node.vote_processor.calculate_weights (); + + ASSERT_EQ (node.vote_processor.representatives_1.end (), node.vote_processor.representatives_1.find (key0.pub)); + ASSERT_EQ (node.vote_processor.representatives_2.end (), node.vote_processor.representatives_2.find (key0.pub)); + ASSERT_EQ (node.vote_processor.representatives_3.end (), node.vote_processor.representatives_3.find (key0.pub)); + + ASSERT_NE (node.vote_processor.representatives_1.end (), node.vote_processor.representatives_1.find (key1.pub)); + ASSERT_EQ (node.vote_processor.representatives_2.end (), node.vote_processor.representatives_2.find (key1.pub)); + ASSERT_EQ (node.vote_processor.representatives_3.end (), node.vote_processor.representatives_3.find (key1.pub)); + + ASSERT_NE (node.vote_processor.representatives_1.end (), node.vote_processor.representatives_1.find (key2.pub)); + ASSERT_NE (node.vote_processor.representatives_2.end (), node.vote_processor.representatives_2.find (key2.pub)); + ASSERT_EQ (node.vote_processor.representatives_3.end (), node.vote_processor.representatives_3.find (key2.pub)); + + ASSERT_NE (node.vote_processor.representatives_1.end (), node.vote_processor.representatives_1.find (nano::test_genesis_key.pub)); + ASSERT_NE (node.vote_processor.representatives_2.end (), node.vote_processor.representatives_2.find (nano::test_genesis_key.pub)); + ASSERT_NE (node.vote_processor.representatives_3.end (), node.vote_processor.representatives_3.find (nano::test_genesis_key.pub)); +} +} diff --git a/nano/node/vote_processor.cpp b/nano/node/vote_processor.cpp index b77766e98b..4d64aeefda 100644 --- a/nano/node/vote_processor.cpp +++ b/nano/node/vote_processor.cpp @@ -233,6 +233,18 @@ void nano::vote_processor::flush () } } +size_t nano::vote_processor::size () +{ + nano::lock_guard guard (mutex); + return votes.size (); +} + +bool nano::vote_processor::empty () +{ + nano::lock_guard guard (mutex); + return votes.empty (); +} + void nano::vote_processor::calculate_weights () { nano::unique_lock lock (mutex); diff --git a/nano/node/vote_processor.hpp b/nano/node/vote_processor.hpp index 0a8e3ef2cc..b750e2eca1 100644 --- a/nano/node/vote_processor.hpp +++ b/nano/node/vote_processor.hpp @@ -38,6 +38,8 @@ class vote_processor final nano::vote_code vote_blocking (std::shared_ptr, std::shared_ptr, bool = false); void verify_votes (std::deque, std::shared_ptr>> const &); void flush (); + size_t size (); + bool empty (); void calculate_weights (); void stop (); @@ -67,6 +69,7 @@ class vote_processor final std::thread thread; friend std::unique_ptr collect_container_info (vote_processor & vote_processor, const std::string & name); + friend class vote_processor_weights_Test; }; std::unique_ptr collect_container_info (vote_processor & vote_processor, const std::string & name);