Skip to content

Commit

Permalink
Add tests for vote_processor (#2574)
Browse files Browse the repository at this point in the history
  • Loading branch information
guilhermelawless authored Feb 20, 2020
1 parent 1de3ea0 commit ae3c3e3
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 0 deletions.
1 change: 1 addition & 0 deletions nano/core_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ add_executable (core_test
uint256_union.cpp
utility.cpp
versioning.cpp
vote_processor.cpp
wallet.cpp
wallets.cpp
websocket.cpp
Expand Down
142 changes: 142 additions & 0 deletions nano/core_test/vote_processor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#include <nano/core_test/testutil.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/node/testing.hpp>
#include <nano/node/vote_processor.hpp>

#include <gtest/gtest.h>

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<nano::vote> (key.pub, key.prv, 1, std::vector<nano::block_hash>{ genesis.open->hash () }));
auto vote_invalid = std::make_shared<nano::vote> (*vote);
vote_invalid->signature.bytes[0] ^= 1;
auto channel (std::make_shared<nano::transport::channel_udp> (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::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, std::vector<nano::block_hash>{ genesis.open->hash () }));
auto channel (std::make_shared<nano::transport::channel_udp> (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<nano::vote> (key.pub, key.prv, 1, std::vector<nano::block_hash>{ genesis.open->hash () }));
auto vote_invalid = std::make_shared<nano::vote> (*vote);
vote_invalid->signature.bytes[0] ^= 1;
auto channel (std::make_shared<nano::transport::channel_udp> (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));
}
}
12 changes: 12 additions & 0 deletions nano/node/vote_processor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,18 @@ void nano::vote_processor::flush ()
}
}

size_t nano::vote_processor::size ()
{
nano::lock_guard<std::mutex> guard (mutex);
return votes.size ();
}

bool nano::vote_processor::empty ()
{
nano::lock_guard<std::mutex> guard (mutex);
return votes.empty ();
}

void nano::vote_processor::calculate_weights ()
{
nano::unique_lock<std::mutex> lock (mutex);
Expand Down
3 changes: 3 additions & 0 deletions nano/node/vote_processor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class vote_processor final
nano::vote_code vote_blocking (std::shared_ptr<nano::vote>, std::shared_ptr<nano::transport::channel>, bool = false);
void verify_votes (std::deque<std::pair<std::shared_ptr<nano::vote>, std::shared_ptr<nano::transport::channel>>> const &);
void flush ();
size_t size ();
bool empty ();
void calculate_weights ();
void stop ();

Expand Down Expand Up @@ -67,6 +69,7 @@ class vote_processor final
std::thread thread;

friend std::unique_ptr<container_info_component> collect_container_info (vote_processor & vote_processor, const std::string & name);
friend class vote_processor_weights_Test;
};

std::unique_ptr<container_info_component> collect_container_info (vote_processor & vote_processor, const std::string & name);
Expand Down

0 comments on commit ae3c3e3

Please sign in to comment.